diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..cc9575f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,95 @@ +name: Python PySDD package + +on: + workflow_dispatch: + release: + types: [published] + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-13, macos-14] + + steps: + - uses: actions/checkout@v4 + - name: Build wheels + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_ARCHS_WINDOWS: auto ARM64 + CIBW_SKIP: cp36-* cp37-* pp* *i686 *ppc64le *s390x *win32* + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: "pytest {project}/tests" + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl + + build_linux_armwheels: + name: Build wheels on Linux Arm with QEMU + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Build wheels + uses: pypa/cibuildwheel@v2.21.3 + env: + CIBW_ARCHS_LINUX: aarch64 + CIBW_SKIP: cp36-* cp37-* pp* *i686 *ppc64le *s390x *win32* + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: "pytest {project}/tests" + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-ubuntu-qemu-arm + path: ./wheelhouse/*.whl + + build_sdist: + name: Prepare source distribution + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.9' + - name: Build + run: | + python -m pip install build + python -m build --sdist + - name: Store artifacts + uses: actions/upload-artifact@v4 + with: + name: dist-source + path: dist/*.tar.gz + + + upload_wheels: + name: Upload wheels to PyPi + runs-on: ubuntu-latest + needs: [ build_wheels,build_sdist,build_linux_armwheels ] + + steps: + - uses: actions/download-artifact@v4 + with: + pattern: dist-* + merge-multiple: true + path: dist + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + packages_dir: dist/ + skip_existing: true diff --git a/.gitignore b/.gitignore index 36fe938..fd53279 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -pysdd/lib/libsdd* #pysdd/lib/sdd* .idea build @@ -14,7 +13,6 @@ _build *.egg-info .eggs dist -pysdd/lib/sdd-2.0/* wm_* venv* __pycache__ diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index c1ffa00..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,9 +0,0 @@ -requirements_file: requirements.txt - -build: - image: latest - -python: - version: 3.6 - setup_py_install: true - diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 161d2f3..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -global-include *.pyx *.pxd *.c *.h *.so *.a *.dll *.lib -include LICENSE \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 9b1b01a..0000000 --- a/Makefile +++ /dev/null @@ -1,55 +0,0 @@ - -LIB=pysdd - -.PHONY: clean -clean: - python3 setup.py clean - rm -f ${LIB}/sdd*.c - rm -f ${LIB}/sdd*.so - rm -fr build/* - -.PHONY: build -build: - python3 setup.py build_ext --inplace - -.PHONY: build_debug -build_debug: clean - python3 setup.py --debug build_ext --inplace - -.PHONY: prepare_dist -prepare_dist: - rm -rf dist/* - python3 setup.py sdist - @#python3 setup.py sdist bdist_wheel - -.PHONY: version -version: - @python3 -c "import pysdd;print(pysdd.__version__)" - -.PHONY: deploy -deploy: prepare_dist - @echo "Check whether repo is clean" - git diff-index --quiet HEAD - @echo "Add tag" - #git tag "v$$(python3 setup.py --version)" - versiontag=$$(python3 -c "import pysdd;print(pysdd.__version__)") && git tag "v$$versiontag" - @echo "Start uploading" - @echo "-> Use Github actions" - # twine upload --repository pysdd dist/* - -.PHONY: docs -docs: - export PYTHONPATH=..; cd docs; make html - -.PHONY: view-docs -view-docs: docs - open docs/_build/html/index.html - -.PHONY: test -test: - export PYTHONPATH=.;python -m pytest --ignore=venv --ignore-glob="venv_*" - -.PHONY: testv -testv: - export PYTHONPATH=.;python -m pytest -s --ignore=venv --ignore-glob="venv_*" - diff --git a/pyproject.toml b/pyproject.toml index 541f6be..363c9ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,120 @@ [build-system] -# Minimum requirements for the build system to execute. -requires = ["setuptools", "wheel", "cython"] # PEP 508 specifications. +requires = ["setuptools", "cython"] +build-backend = "setuptools.build_meta" + +[project] +name = "PySDD" +version = "1.0" +description = "Sentential Decision Diagrams" +authors = [ + { name = "Wannes Meert", email = "wannes.meert@cs.kuleuven.be" }, + { name = "Arthur Choi" }, +] +readme = "README.rst" +license = { file = "LICENSE" } +requires-python = ">=3.8" +dependencies = [ + "cython", + "numpy", +] +keywords = ["sdd, knowledge compilation"] +classifiers = [ + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Artificial Intelligence" +] + +[project.optional-dependencies] +tests = ["pytest"] + + + +[project.urls] +Homepage = "https://github.com/wannesm/PySDD" +Documentation = "https://pysdd.readthedocs.io/en/latest/" + +[project.entry-points."console_scripts"] +pysdd = "pysdd.cli:main" + +[tool.setuptools] +packages=["pysdd"] + +[[tool.setuptools.ext-modules]] +name = "pysdd.sdd" +sources = [ + "pysdd/sdd.pyx", + "pysdd/lib/libsdd-2.0/src/util.c", + "pysdd/lib/libsdd-2.0/src/verify.c", + "pysdd/lib/libsdd-2.0/src/version.c", + "pysdd/lib/libsdd-2.0/src/basic/computed.c", + "pysdd/lib/libsdd-2.0/src/basic/count_and_size.c", + "pysdd/lib/libsdd-2.0/src/basic/gc.c", + "pysdd/lib/libsdd-2.0/src/basic/hash.c", + "pysdd/lib/libsdd-2.0/src/basic/memory.c", + "pysdd/lib/libsdd-2.0/src/basic/multiply.c", + "pysdd/lib/libsdd-2.0/src/basic/nodes.c", + "pysdd/lib/libsdd-2.0/src/basic/partitions.c", + "pysdd/lib/libsdd-2.0/src/basic/references.c", + "pysdd/lib/libsdd-2.0/src/basic/replace.c", + "pysdd/lib/libsdd-2.0/src/basic/shadows.c", + "pysdd/lib/libsdd-2.0/src/basic/sort.c", + "pysdd/lib/libsdd-2.0/src/manager/copy.c", + "pysdd/lib/libsdd-2.0/src/manager/interface.c", + "pysdd/lib/libsdd-2.0/src/manager/manager.c", + "pysdd/lib/libsdd-2.0/src/manager/stats.c", + "pysdd/lib/libsdd-2.0/src/manager/variables.c", + "pysdd/lib/libsdd-2.0/src/sdds/apply.c", + "pysdd/lib/libsdd-2.0/src/sdds/bits.c", + "pysdd/lib/libsdd-2.0/src/sdds/cardinality.c", + "pysdd/lib/libsdd-2.0/src/sdds/condition.c", + "pysdd/lib/libsdd-2.0/src/sdds/copy.c", + "pysdd/lib/libsdd-2.0/src/sdds/count.c", + "pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c", + "pysdd/lib/libsdd-2.0/src/sdds/exists.c", + "pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c", + "pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c", + "pysdd/lib/libsdd-2.0/src/sdds/forall.c", + "pysdd/lib/libsdd-2.0/src/sdds/io.c", + "pysdd/lib/libsdd-2.0/src/sdds/model_count.c", + "pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c", + "pysdd/lib/libsdd-2.0/src/sdds/size.c", + "pysdd/lib/libsdd-2.0/src/sdds/wmc.c", + "pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c", + "pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c", + "pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c", + "pysdd/lib/libsdd-2.0/src/vtree_operations/split.c", + "pysdd/lib/libsdd-2.0/src/vtree_search/auto.c", + "pysdd/lib/libsdd-2.0/src/vtree_search/search.c", + "pysdd/lib/libsdd-2.0/src/vtree_search/state.c", + "pysdd/lib/libsdd-2.0/src/vtrees/compare.c", + "pysdd/lib/libsdd-2.0/src/vtrees/edit.c", + "pysdd/lib/libsdd-2.0/src/vtrees/io.c", + "pysdd/lib/libsdd-2.0/src/vtrees/maps.c", + "pysdd/lib/libsdd-2.0/src/vtrees/moves.c", + "pysdd/lib/libsdd-2.0/src/vtrees/static.c", + "pysdd/lib/libsdd-2.0/src/vtrees/vtree.c", + "pysdd/lib/sdd-2.0/src/fnf/compiler.c", + "pysdd/lib/sdd-2.0/src/fnf/fnf.c", + "pysdd/lib/sdd-2.0/src/fnf/io.c", + "pysdd/lib/sdd-2.0/src/fnf/utils.c", +] +include-dirs = [ + "pysdd/lib/libsdd-2.0/include", + "pysdd/lib/sdd-2.0/include", + "pysdd/lib/sdd_extra/include", +] + +[tool.cibuildwheel] +environment = {CFLAGS="-std=c99 -O2 -finline-functions" } + +[tool.cibuildwheel.windows] +environment = {CL = "/O2"} diff --git a/pysdd/lib/.gitignore b/pysdd/lib/.gitignore new file mode 100644 index 0000000..fd13349 --- /dev/null +++ b/pysdd/lib/.gitignore @@ -0,0 +1,3 @@ +.vs +.sconsign.dblite +*/__pycache__/* \ No newline at end of file diff --git a/pysdd/lib/libsdd-2.0/LICENSE b/pysdd/lib/libsdd-2.0/LICENSE new file mode 100644 index 0000000..a5fab9f --- /dev/null +++ b/pysdd/lib/libsdd-2.0/LICENSE @@ -0,0 +1,13 @@ +Copyright 2013-2018, Regents of the University of California + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pysdd/lib/libsdd-2.0/README b/pysdd/lib/libsdd-2.0/README new file mode 100644 index 0000000..5f7216d --- /dev/null +++ b/pysdd/lib/libsdd-2.0/README @@ -0,0 +1,54 @@ +The Sentential Decision Diagram Package +sdd version 2.0, January 8, 2018 +http://reasoning.cs.ucla.edu/sdd + +This directory provides the source code for the SDD library. + +All source code provided is in the C programming language (and in +particular, C99). + +COMPILATION + +The SDD library uses the SCons build tool for compiling the library +(see http://scons.org). If SCons is installed on the system, the +library can be compiled by running the following on the command line: + + scons + +(run this command in the same directory that contains the file +SConstruct). This command compiles a static library (named libsdd.a) +and a shared library (named libsdd.so on Linux, and named libsdd.dylib +on Mac). Both will be found under the build directory. The following +command: + + scons mode=debug + +produces libraries with debugging information included, found under +the debug directory. Adding a -c flag to either of the above commands +will clean the respective build. + +The debug build will enable assertions by default. To disable these +assertions, run the command: + + scons mode=debug --disable-assertions + +A more expensive but more exhaustive debugging mode can be enabled by +running the following command: + + scons mode=debug --enable-full-debug + +These options are ignored when compiling the library without the +mode=debug option. + +AUTHORS + +The SDD Package was developed by Arthur Choi and Adnan Darwiche, of +the Automated Reasoning Group at the University of California, Los +Angeles. + + http://reasoning.cs.ucla.edu + +Feedback, bug reports, and questions can be sent to the email address + + sdd@cs.ucla.edu + diff --git a/pysdd/lib/libsdd-2.0/include/iterators.h b/pysdd/lib/libsdd-2.0/include/iterators.h new file mode 100644 index 0000000..2b7ac00 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/include/iterators.h @@ -0,0 +1,243 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#ifndef ITERATORS_H_ +#define ITERATORS_H_ + +/**************************************************************************************** + * iterator over ALL vtree nodes (linked using ->next, ->prev, ->first and ->last) + ****************************************************************************************/ + +//iterates over each node V in a vtree rooted at R, executing code B for each node +// +//V: vtree node +//R: vtree root +//B: code +#define FOR_each_vtree_node(V,R,B) {\ + Vtree* V = (R)->first; /* head of linked list */\ + Vtree* _out = (R)->last->next; /* node following last node of vtree (can be NULL) */\ + while(V!=_out) {\ + assert(V);\ + B; /* execute code */\ + V = V->next; /* next node of vtree */\ + }\ +} + +/**************************************************************************************** + * iterator over INTERNAL vtree nodes (linked using ->next, ->prev, ->first and ->last) + * + * the nodes of a vtree linked list alternate between leaf and internal nodes, with + * the first and last nodes being leaves + ****************************************************************************************/ + +//iterates over each internal node V in a vtree rooted at R, executing code B for each node +// +//V: vtree node +//R: vtree root +//B: code +#define FOR_each_internal_vtree_node(V,R,B) {\ + Vtree* V = (R)->first; /* first leaf node of vtree */\ + Vtree* _last_leaf = (R)->last; /* last leaf node of vtree */\ + while(V!=_last_leaf) {\ + V = V->next; /* V is internal */\ + assert(INTERNAL(V));\ + B; /* execute code */\ + V = V->next; /* V is leaf */\ + assert(LEAF(V));\ + }\ +} + +/**************************************************************************************** + * iterator over LEAF vtree nodes (linked using ->next, ->prev, ->first and ->last) + * + * the nodes of a vtree linked list alternate between leaf and internal nodes, with + * the first and last nodes being leaves + ****************************************************************************************/ + +//iterates over each leaf node V in a vtree rooted at R, executing code B for each node +// +//V: vtree node +//R: vtree root +//B: code +#define FOR_each_leaf_vtree_node(V,R,B) {\ + Vtree* V = (R)->first; /* first leaf node of vtree */\ + Vtree* _last_leaf = (R)->last; /* last leaf node of vtree */\ + while(V!=_last_leaf) {\ + assert(LEAF(V));\ + B; /* execute code */\ + V = V->next->next; /* V is leaf */\ + }\ + assert(LEAF(V));\ + B; /* execute code on last leaf */\ +} + +/**************************************************************************************** + * iterator over ANCESTOR vtree nodes (visiting children before parents) + ****************************************************************************************/ + +//iterates over each ancestor V in a vtree rooted at R, executing code B for each node +// +//V: vtree node +//R: vtree root +//B: code +#define FOR_each_ancestral_vtree_node(V,R,B) {\ + Vtree* V = (R)->parent;\ + while(V) {\ + B; /* execute code */\ + V = V->parent;\ + }\ +} + + +/**************************************************************************************** + * iterator over nodes normalized for a vtree + * (linked using the ->vtree_next and ->vtree_prev fields) + ****************************************************************************************/ + +//iterate over the sdd nodes normalized for vtree node V, executing code B for each node +//this iterator allows freeing sdd nodes or changing their ->vtree_next and ->vtree_prev +//fields in code B +// +//N: node +//V: vtree +//B: code +#define FOR_each_sdd_node_normalized_for(N,V,B) {\ + SddNode* N = (V)->nodes; /* head of linked list */\ + while(N) {\ + /* save N->vtree_next in case N is freed or N->vtree_next is changed in code B */\ + SddNode* _next = N->vtree_next;\ + /* execute code */\ + B;\ + /* advance to next node */\ + N = _next;\ + }\ +} + +/**************************************************************************************** + * iterator over sdd nodes normalized for nodes in vtree + ****************************************************************************************/ + +//N: node +//V: vtree +//B: code +#define FOR_each_decomposition_in(N,V,B) FOR_each_internal_vtree_node(v,V,FOR_each_sdd_node_normalized_for(N,v,B)) +#define FOR_each_decomposition_above(N,V,B) FOR_each_ancestral_vtree_node(v,V,FOR_each_sdd_node_normalized_for(N,v,B)) +#define FOR_each_literal_in(N,V,B) FOR_each_leaf_vtree_node(v,V,FOR_each_sdd_node_normalized_for(N,v,B)) + +/**************************************************************************************** + * iterator for sdd nodes (linked using the ->next field) + ****************************************************************************************/ + +//this iterator allows freeing nodes or changing their ->next field in code B +// +//N: node +//L: head of linked list (node) +//B: code +#define FOR_each_linked_node(N,L,B) {\ + SddNode* N = L; /* head of linked list */\ + while(N) {\ + /* save N->next in case N is freed or N->next is changed in code B */\ + SddNode* _next = N->next;\ + /* execute code */\ + B;\ + /* advance to next node */\ + N = _next;\ + }\ +} + +/**************************************************************************************** + * iterator for unique nodes + ****************************************************************************************/ + +//iterates over unique nodes of manager, executing a bit of code for each node +//this iterator allows freeing nodes or changing their ->next field in code B +// +//N: node +//M: manager +//B: code to be executed for each node (references the variable E) +#define FOR_each_unique_node(N,M,B) {\ + SddHash* H = M->unique_nodes;\ + if(H->count) {\ + SddSize _size = H->size;\ + SddNode** _clists = H->clists;\ + while(_size--) {\ + SddNode* N = *_clists++;\ + while(N) {\ + /* save next in case N is freed or N->next is changed in code B */\ + SddNode* _next = N->next;\ + /* execute code */\ + B;\ + /* advance to next entry in hash table */\ + N = _next;\ + }\ + }\ + }\ +} + +/**************************************************************************************** + * macros for iterating over primes, subs and elements of an sdd node + ****************************************************************************************/ + +//iterate over elements, executing code B for each element +// +//P: prime +//S: sub +//C: element count +//ES: elements array +//B: code +#define FOR_each_prime_sub_of_elements(P,S,C,ES,B) {\ + for(SddElement* _e=ES; _e<(ES)+(C); _e++) {\ + SddNode* P = _e->prime;\ + SddNode* S = _e->sub;\ + /* execute code */\ + B;\ + }\ +} + +//iterate over primes and subs P/S of sdd node N, executing code B for each P/S pair +// +//P: prime +//S: sub +//N: sdd node +//B: code +#define FOR_each_prime_sub_of_node(P,S,N,B) {\ + assert(IS_DECOMPOSITION(N));\ + FOR_each_prime_sub_of_elements(P,S,(N)->size,ELEMENTS_OF(N),B)\ +} + +//iterate over primes P of sdd node N, executing code B for each prime +// +//P: prime +//N: sdd node +//B: code +#define FOR_each_prime_of_node(P,N,B) {\ + assert(IS_DECOMPOSITION(N));\ + for(SddElement* _e=ELEMENTS_OF(N); _esize; _e++) {\ + SddNode* P = _e->prime;\ + /* execute code */\ + B;\ + }\ +} + +//iterate over subs S of sdd node N, executing code B for each sub +// +//S: sub +//N: sdd node +//B: code +#define FOR_each_sub_of_node(S,N,B) {\ + assert(IS_DECOMPOSITION(N));\ + for(SddElement* _e=ELEMENTS_OF(N); _esize; _e++) {\ + SddNode* S = _e->sub;\ + /* execute code */\ + B;\ + }\ +} + +#endif // ITERATORS_H_ + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/include/macros.h b/pysdd/lib/libsdd-2.0/include/macros.h new file mode 100644 index 0000000..c4e31ac --- /dev/null +++ b/pysdd/lib/libsdd-2.0/include/macros.h @@ -0,0 +1,273 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#ifndef MACROS_H_ +#define MACROS_H_ + +/**************************************************************************************** + * error checks and messages + ****************************************************************************************/ + +#define ERR_MSG_GC "\nerror in %s: accessing sdd node that has been garbage collected\n" +#define ERR_MSG_DEREF "\nerror in %s: more dereferences than references to an sdd node\n" +#define ERR_MSG_INVALID_VAR "\nerror in %s: invalid var index\n" +#define ERR_MSG_VTREE "\nerror in %s: unrecognized vtree type\n" +#define ERR_MSG_INPUT_VTREE "\nerror in %s: must supply a vtree\n" +#define ERR_MSG_ONE_VAR "\nerror in %s: manager must have at least one variable\n" +#define ERR_MSG_TWO_VARS "\nerror in %s: manager must have at least two variables\n" +#define ERR_MSG_REM_VAR "\nerror in %s: removing a variable that is currently being used\n" +#define ERR_MSG_MOV_VAR "\nerror in %s: moving a variable that is currently being used\n" +#define ERR_MSG_NODE_ITR "\nerror in %s: argument not a decision node\n" +#define ERR_MSG_NODE_LIT "\nerror in %s: argument not a literal node\n" +#define ERR_MSG_WMC "\nerror in %s: WMC manager is no longer valid due to automatic SDD minimization\n" +#define ERR_MSG_FRG_N "\nerror in %s: fragment cannot be moved to the next state while in goto mode\n" +#define ERR_MSG_FRG_G "\nerror in %s: fragment cannot by moved to the given state while in next mode\n" +#define ERR_MSG_FRG_R "\nerror in %s: fragment cannot be rewinded while in goto mode\n" +#define FULL_DEBUG FALSE + +//if condition C is met, print error message M that materialized in function F +#define CHECK_ERROR(C,M,F) if(C) { fprintf(stderr,M,F); exit(1); } + +/**************************************************************************************** + * tests and lookups + ****************************************************************************************/ + +//OP: constant, which is either CONJOIN or DISJOIN +//M: manager +#define ZERO(M,OP) (OP==CONJOIN? (M)->false_sdd: (M)->true_sdd) +#define ONE(M,OP) (OP==CONJOIN? (M)->true_sdd: (M)->false_sdd) + +//OP: constant, which is either CONJOIN or DISJOIN +//N: node +#define IS_ZERO(N,OP) (OP==CONJOIN? (N)->type==FALSE: (N)->type==TRUE) +#define IS_ONE(N,OP) (OP==CONJOIN? (N)->type==TRUE: (N)->type==FALSE) + +//OP: constant, which is either CONJOIN or DISJOIN +//N: sdd node +#define IS_FALSE(N) ((N)->type==FALSE) +#define IS_TRUE(N) ((N)->type==TRUE) +#define IS_LITERAL(N) ((N)->type==LITERAL) +#define IS_DECOMPOSITION(N) ((N)->type==DECOMPOSITION) + +//checks that an sdd node is neither true nor false +//N: sdd node +#define NON_TRIVIAL(N) ((N)->type!=FALSE && (N)->type!=TRUE) +#define TRIVIAL(N) ((N)->type==FALSE || (N)->type==TRUE) + +//checks whether node is on the gc_list (i.e., has been garbage collected) +//N: Sdd node +#define GC_NODE(N) (N->id==0) + +//recovering fields of nodes +#define ELEMENTS_OF(N) (N)->alpha.elements +#define LITERAL_OF(N) (N)->alpha.literal +#define VAR_OF(N) labs((N)->alpha.literal) + +//checking whether an sdd node is live +//terminal sdds are always live +#define LIVE(N) ((N)->type!=DECOMPOSITION || (N)->ref_count) +#define DEAD(N) ((N)->type==DECOMPOSITION && (N)->ref_count==0) + +//tests whether a variable index is valid +//V: var index +//M: manager +#define VALID_VAR(V,M) (1 <= V && V <= M->var_count) + +//a function is provided for this in the sddapi, but a macro is used internally for efficiency +#define LEAF(V) ((V)->left==NULL) +#define INTERNAL(V) ((V)->left) + +//checks whether an sdd node N could be a valid prime/sub for vtree V +#define OK_PRIME(P,V) (NON_TRIVIAL(P) && sdd_vtree_is_sub((P)->vtree,(V)->left)) +#define OK_SUB(S,V) (TRIVIAL(S) || sdd_vtree_is_sub((S)->vtree,(V)->right)) + +#define Assert(C)\ +if(!(C)) {\ + void print_trace ();\ + print_trace();\ + assert(C);\ +} + +/**************************************************************************************** + * macro for constructing sdd nodes (see basic/partitions.c) + ****************************************************************************************/ + +//sets N to sdd node with a compression of the elements added in code B +//if sdd node has form T.n, set N to n (trimming rule 1) +//if sdd node has form n.T + ~n.F, set N to n (trimming rule 2) +// +//the resulting node will be normalized to vtree V unless one of the trimming rules applies +// +//N: node +//V: vtree +//M: manager +//B: code (uses DECLARE_element(prime,sub,manager) to add elements) +// +//this macro may invoke apply +#define GET_node_from_partition(N,V,M,B) {\ + void START_partition(struct sdd_manager_t* manager);\ + void DECLARE_element(struct sdd_node_t* prime, struct sdd_node_t* sub, struct vtree_t* vtree, struct sdd_manager_t* manager);\ + SddNode* GET_node_of_partition(struct vtree_t* vtree, struct sdd_manager_t* manager, int limited);\ + START_partition(M);\ + B; /* execute code */\ + N = GET_node_of_partition(V,M,0);\ + assert(N);\ +} + +//this is used ONLY inside GET_node_from_partition_limited +//C: test for exceeding limit and aborting GET_node_from_partition_limited +#define ABORT_partition_if(C) if(C) goto abort + +//T: time limit +//this macro may invoke apply +#define GET_node_from_partition_limited(N,V,M,T,B) {\ + void START_partition(struct sdd_manager_t* manager);\ + void DECLARE_element(struct sdd_node_t* prime, struct sdd_node_t* sub, struct vtree_t* vtree, struct sdd_manager_t* manager);\ + void ABORT_partition(struct sdd_manager_t* manager);\ + SddNode* GET_node_of_partition(struct vtree_t* vtree, struct sdd_manager_t* manager, int limited);\ + START_partition(M);\ + B; /* execute code: may jump to abort */\ + N = GET_node_of_partition(V,M,T); /* N may be NULL */\ + goto done;\ + abort:\ + N = NULL;\ + ABORT_partition(M);\ + done:\ + /* N may or may not be NULL */\ + ;\ +} + +//sets N to sdd node with elements declared in code B +//assumes elements already compressed, cannot be trimmed and normalized for vtree +// +//N: node +//V: vtree +//M: manager +//B: code (uses DECLARE_compressed_element(prime,sub,manager) to add elements) +// +//this macro will not invoke apply +#define GET_node_from_compressed_partition(N,V,M,B) {\ + void START_partition(struct sdd_manager_t* manager);\ + void DECLARE_compressed_element(struct sdd_node_t* prime, struct sdd_node_t* sub, struct vtree_t* vtree, struct sdd_manager_t* manager);\ + SddNode* GET_node_of_compressed_partition(struct vtree_t* vtree, struct sdd_manager_t* manager);\ + START_partition(M);\ + B; /* execute code */\ + N = GET_node_of_compressed_partition(V,M);\ +} + +/**************************************************************************************** + * macro for auto gc and search + ****************************************************************************************/ + +//ensures that code B of manager M will run without ever invoking vtree search +// +//M: manager +//B: code +#define WITH_no_auto_mode(M,B) {\ + int _mode = M->auto_gc_and_search_on; /* save auto-search mode */\ + M->auto_gc_and_search_on = 0; /* deactivate auto-search */\ + B; /* execute code */\ + M->auto_gc_and_search_on = _mode; /* recover auto-search mode */\ +} + +//ensures that if code B of manager M invokes gc or vtree search, then gc/search +//will only take place locally (and not globally) +// +//M: manager +//B: code +#define WITH_local_auto_mode(M,B) {\ + int _mode = M->auto_local_gc_and_search_on;\ + M->auto_local_gc_and_search_on = 1;\ + B; /* execute code */\ + M->auto_local_gc_and_search_on = _mode;\ +} + +/**************************************************************************************** + * macro for timing code + ****************************************************************************************/ + +//C: variable that accumulates time +//B: code +#define WITH_timing(C,B) {\ + clock_t start_time = clock();\ + B; /* execute code */\ + C += (clock()-start_time); /* accumulate time*/\ +} + +/**************************************************************************************** + * memory allocation, with error catching + ****************************************************************************************/ + +//catching malloc and calloc errors +#define MALLOC(variable,type,message) {\ + variable = (type*) malloc(sizeof(type));\ + if(variable==NULL) {\ + fprintf(stderr,"\nmalloc failed in %s\n",message);\ + exit(1);\ + }\ +} + +#define CALLOC(variable,type,count,message) {\ + if(count==0) variable = NULL;\ + else {\ + if((variable=(type*) calloc(count,sizeof(type)))==NULL) {\ + fprintf(stderr,"\ncalloc failed in %s\n",message);\ + exit(1);\ + }\ + }\ +} + +#define REALLOC(variable,type,count,message) {\ + variable = (type*) realloc(variable,(count)*sizeof(type));\ + if(variable==NULL) {\ + fprintf(stderr,"\nrealloc failed in %s\n",message);\ + exit(1);\ + }\ +} + + +/**************************************************************************************** + * general utilities + ****************************************************************************************/ + +//compute max of A and B +// +//A: number +//B: number +#define MAX(A,B) (((A)>(B))?(A):(B)) + +//compute min of A and B +// +//A: number +//B: number +#define MIN(A,B) (((A)<(B))?(A):(B)) + +//swapping the values of two variables +// +//T: type of vars +//V1: first var +//V2: second var +#define SWAP(T,V1,V2) { T V = V1; V1 = V2; V2 = V; } + +//compute the space in megabytes for N members of type T +// +//N: number of elements of type T +//T: type +#define TYPE2MB(N,T) (((float)(N)*sizeof(T))/(1024*1024)) + +//add value to array whose elements are of type and has size +#define ADD_TO_ARRAY(type,value,array,size) {\ + ++(size);\ + REALLOC(array,type,size,"ADD_TO_ARRAY");\ + array[(size)-1] = value;\ +} + +#endif // MACROS_H_ + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/include/parameters.h b/pysdd/lib/libsdd-2.0/include/parameters.h new file mode 100644 index 0000000..c0d774f --- /dev/null +++ b/pysdd/lib/libsdd-2.0/include/parameters.h @@ -0,0 +1,71 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#ifndef PARAMETERS_H_ +#define PARAMETERS_H_ + +/**************************************************************************************** + * garbage collection + ****************************************************************************************/ + +//buckets for storing gc'd nodes according to their size +#define GC_BUCKETS_COUNT 4 + +/**************************************************************************************** + * manager stacks + ****************************************************************************************/ + +#define INITIAL_SIZE_ELEMENT_STACK 2048 +#define INITIAL_SIZE_COMPRESSION_STACK 2048 +#define INITIAL_SIZE_NODE_BUFFER 2048 + +/**************************************************************************************** + * hash table parameters (for unique nodes) + ****************************************************************************************/ + +//default qsize of unique node table +#define INITIAL_SIZE_UNIQUE_NODE_TABLE 0 + +//number of lookups to trigger check for resize +#define AGE_BEFORE_HASH_RESIZE_CHECK 100 +//number of entries over size that would trigger an increase of hash size +#define LOAD_TO_INCREASE_HASH_SIZE 0.80 +//number of entries over size that would trigger a decrease of hash size +#define LOAD_TO_DECREASE_HASH_SIZE 0.05 + +/**************************************************************************************** + * computation cache parameters + ****************************************************************************************/ + +//640007, 1280023, 2560021, 5000011 +#define COMPUTED_CACHE_SIZE 2560021 + +/**************************************************************************************** + * vtree search options + ****************************************************************************************/ + +//these parameters haver setter functions +#define VTREE_SEARCH_TIME_LIMIT (clock_t) 180*CLOCKS_PER_SEC +#define VTREE_FRAGMENT_TIME_LIMIT (clock_t) 60*CLOCKS_PER_SEC +#define VTREE_OP_TIME_LIMIT (clock_t) 30*CLOCKS_PER_SEC +#define VTREE_APPLY_TIME_LIMIT (clock_t) 10*CLOCKS_PER_SEC +#define VTREE_OP_SIZE_LIMIT 1.2 +#define VTREE_OP_MEMORY_LIMIT 3.0 +#define INITIAL_CONVERGENCE_THRESHOLD 1.0 +#define CARTESIAN_PRODUCT_LIMIT 8*1024 + +//these parameters do not have setter functions +#define LIMITS_CHECK_FREQUENCY 100 +#define VTREE_OP_SIZE_MIN 16 +#define VTREE_OP_MEMORY_MIN 100.0 + +#define FRAGMENT_SEARCH_BACKWARD 0 + +#endif // PARAMETERS_H_ + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/include/sdd.h b/pysdd/lib/libsdd-2.0/include/sdd.h new file mode 100644 index 0000000..b00a6f1 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/include/sdd.h @@ -0,0 +1,823 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +//#include +#include "parameters.h" +#include "macros.h" +#include "iterators.h" +#include "stacks.h" + +#ifndef SDD_H_ +#define SDD_H_ + +/**************************************************************************************** + * typedefs and printf controls + ****************************************************************************************/ + +//sdd types +typedef size_t SddSize; //number of nodes, sizes of hash tables, etc +typedef size_t SddNodeSize; //size of decomposition for sdd nodes +typedef size_t SddRefCount; //refcount +typedef unsigned long long SddModelCount; //model count +typedef double SddWmc; // weighted model count +typedef long long SddLiteral; //literals of clauses + +//control strings +#define PRIsS "zu" +#define PRInsS "u" +#define PRIrcS "u" +#define PRImcS "llu" +#define PRIwmcS "f" +#define PRIlitS "ld" + +/**************************************************************************************** + * Enumerated Types + ****************************************************************************************/ + +typedef char SddNodeType; //holds one of two values defined next +#define FALSE 0 +#define TRUE 1 +#define LITERAL 2 +#define DECOMPOSITION 3 + +typedef unsigned short BoolOp; //holds one of two values defined next +#define CONJOIN 0 +#define DISJOIN 1 + + +/**************************************************************************************** + * vtree + ****************************************************************************************/ + +// local search state, stored at each vtree node +typedef struct { + struct vtree_t* previous_left; + struct vtree_t* previous_right; + SddSize previous_size; + SddSize previous_count; + unsigned fold:1; + unsigned virtually_empty:1; +} VtreeSearchState; + +//vtree is a complete binary tree +typedef struct vtree_t { + struct vtree_t* parent; //parent + struct vtree_t* left; //left child + struct vtree_t* right; //right child + + //vtree nodes are maintained as a linked list + struct vtree_t* next; //next node in in-order + struct vtree_t* prev; //previous node in in-order + struct vtree_t* first; //first node in in-order (which is part of this vtree) + struct vtree_t* last; //last node in in-order (which is part of this vtree) + + //position of vtree node in the vtree inorder + //position may CHANGE, e.g., due to swapping or adding/removing/moving variables, + //but is invariant to rotations + SddLiteral position; //start from 0 + + SddLiteral var_count; //number of variables in vtree + SddSize sdd_size; //sum of sizes for all sdd nodes normalized for this vnode + SddSize dead_sdd_size; //sum of sizes for all dead sdd nodes normalized for this vnode + SddSize node_count; //number of sdd nodes normalized for vtree + SddSize dead_node_count; //number of sdd nodes normalized for vtree with ref_count==0 + + SddLiteral var; //variable associated with vtree (for leaf vtrees only) + + struct sdd_node_t* nodes; //linked list of nodes normalized for vtree (linked using ->vtree_next) + //only two sdd nodes for leaf vtrees: first is positive literal, second is negative literal + + //used to associate secondary data with vtree structures by user + void* user_data; + void* user_search_state; + + //vtree search + SddSize auto_last_search_live_size; + VtreeSearchState* search_state; //for library version of vtree search + + unsigned some_X_constrained_vars:1; + unsigned all_vars_in_sdd:1; + unsigned no_var_in_sdd:1; + unsigned bit:1; + unsigned user_bit:1; //for user convenience +} Vtree; + +/**************************************************************************************** + * FNF: Flat Normal Form (CNF, DNF) +****************************************************************************************/ + +typedef struct { + SddSize id; + SddLiteral literal_count; + SddLiteral* literals; + BoolOp op; //DISJOIN (clause) or CONJOIN (term) + Vtree* vtree; + unsigned bit:1; +} LitSet; + +typedef struct { + SddLiteral var_count; // number of variables + SddSize litset_count; // number of literal sets + LitSet* litsets; // array of literal sets + BoolOp op; //CONJOIN (CNF) or DISJOIN (DNF) +} Fnf; + +typedef Fnf Cnf; +typedef Fnf Dnf; + +/**************************************************************************************** + * SDD nodes + ****************************************************************************************/ + +//size should not be used to iterate over elements: use macros for iteration +//order of fields below is important for the sizeof(SddNode) +typedef struct sdd_node_t { + //put small types next to each other to reduce space used by structure + //these are also the more used fields (more efficient if put first?) + SddNodeType type; + char shadow_type; + SddNodeSize size; //number of elements for decomposition nodes, 0 for terminal nodes + SddNodeSize saved_size; //used for reversing node replacement + SddRefCount ref_count; //number of parents elements that have non-zero ref_count + SddRefCount parent_count; //number of parents for node in the SDD DAG + + union { + struct sdd_element_t* elements; // for decompositions + SddLiteral literal; // for literal terminals + } alpha; + struct sdd_element_t* saved_elements; //used for reversing node replacement + + struct sdd_node_t* next; //linking into collision list of hash table + struct sdd_node_t** prev; //linking into collision list of hash table + struct sdd_node_t* vtree_next; //linking into list of nodes normalized for same vtree + struct sdd_node_t** vtree_prev; //linking into list of nodes normalized for same vtree + struct sdd_node_t* negation; //caches negation of node when it exists + Vtree* vtree; //vtree for which node is normalize for + + SddSize id; //unique id for each node + SddSize index; //used mainly by graph traversal algorithms to cache results + + struct sdd_node_t* multiply_sub; //used by multiply-decompositions + struct sdd_node_t* map; //used for caching node transformations (exists, condition, rename vars,...) + //used also as a next field for distributing sdd nodes over vtree nodes + struct sdd_node_shadow_t* shadow; //used by fragments + + unsigned bit:1; //used for navigating sdd graphs (should be kept 0 by default) + unsigned cit:1; //used for navigating sdd graphs (should be kept 0 by default) + unsigned dit:1; //used for multiplying decompositions + unsigned git:1; //used for garbage collection + unsigned in_unique_table:1; //used for maintaining counts and sizes + unsigned replaced:1; //used for marking rotated or swapped nodes + unsigned user_bit:1; //for user convenience +} SddNode; + +/**************************************************************************************** + * SDD elements + ****************************************************************************************/ + +typedef struct sdd_element_t { + SddNode* prime; + SddNode* sub; +} SddElement; + +/**************************************************************************************** + * SDD Computed + ****************************************************************************************/ + +typedef struct sdd_computed_t { + struct sdd_node_t* result; + SddSize id; //for result + SddSize id1; //for argument1 + SddSize id2; //for argument2 +} SddComputed; + +/**************************************************************************************** + * hash tables for nodes + ****************************************************************************************/ + +typedef struct sdd_hash_t { + int qsize; //qualitative size of hash table + SddSize size; //number of collision lists + SddSize count; //number of entries in hash table + SddSize lookup_count; //number of lookup requests + SddSize hit_count; //number of hists (lookups that succeeded) + SddSize increase_size_count; //number of times size of hash table increased + SddSize decrease_size_count; //number of times size of hash table decreased + SddSize resize_age; //age since last resize + SddSize lookup_cost; //total cost of lookups + SddNode** clists; //array of collision lists + //members of a collision list are linked through ->next and ->prev +} SddHash; + + +/**************************************************************************************** + * shadows + ****************************************************************************************/ + +typedef struct sdd_element_shadow_t { + struct sdd_node_shadow_t* prime; + struct sdd_node_shadow_t* sub; +} ElmShadow; + +typedef struct sdd_node_shadow_t { + union { + ElmShadow* elements; + SddNode* node; + } alpha; + SddNode* cache; + SddSize cache_id; + Vtree* vtree; + SddNodeSize size; + SddRefCount ref_count; + unsigned bit:1; //used for navigating shadows + unsigned reuse:1; //used for shadows that require reusing sdd node +} NodeShadow; + +typedef struct sdd_shadows_t { + struct sdd_manager_t* manager; + SddSize root_count; + NodeShadow** root_shadows; //roots of the shadow DAG + SddSize shadow_count; + SddSize shadow_byte_count; + unsigned bit:1; //current value of shadow bit +} SddShadows; + +/**************************************************************************************** + * vtree fragment + ****************************************************************************************/ + +typedef struct vtree_fragment_t { + + //state + int state; //0...11 + char mode; //'i', 'n', or 'g' + Vtree* cur_root; + Vtree* cur_child; + + //fragment identity + struct sdd_manager_t* manager; + char type; //'l' or 'r' + Vtree* root; + Vtree* child; + char* moves; + + //see definitions of IR, IC and Ic nodes in vtree_fragments/construction.c + SddSize IR_IC_count; + SddSize Ic_count; + SddNode** IR_IC_nodes; + SddNode** Ic_nodes; + + SddShadows* shadows; +} VtreeFragment; + + +/**************************************************************************************** + * manager + ****************************************************************************************/ + +typedef struct sdd_manager_stats_t { + clock_t auto_search_time; + clock_t auto_max_search_time; + SddSize apply_count; + SddSize apply_count_top; + //largest decomposition + SddNodeSize max_decomposition_size; + SddNodeSize max_uncompressed_decomposition_size; + //elements + SddSize element_count; //number of elements in memory + SddSize max_element_count; //maximum number of elements that every existed in memory +} SddManagerStats; + +//options and stats for vtree operations (rotate and swap) +typedef struct sdd_manager_vtree_ops_t { + //time limits + clock_t search_time_limit; + clock_t fragment_time_limit; + clock_t op_time_limit; + clock_t apply_time_limit; + //time stamps + clock_t search_time_stamp; + clock_t fragment_time_stamp; + clock_t op_time_stamp; + clock_t apply_time_stamp; + //aborted + int search_aborted; + int fragment_aborted; + int op_aborted; + int apply_aborted; + //size limits + SddSize op_size_stamp; + SddSize outside_size; + float op_size_limit; + //memory limits + float op_memory_stamp; + float op_memory_limit; + //counts for moves + SddSize lr_count; + SddSize rr_count; + SddSize sw_count; + SddSize failed_lr_count_time; + SddSize failed_rr_count_time; + SddSize failed_sw_count_time; + SddSize failed_lr_count_size; + SddSize failed_rr_count_size; + SddSize failed_sw_count_size; + SddSize failed_count_cp; + SddSize failed_lr_count_memory; + SddSize failed_rr_count_memory; + SddSize failed_sw_count_memory; + //for printing status info (^\) + SddLiteral current_vtree; + char current_op; + float convergence_threshold; + SddSize cartesian_product_limit; +} SddManagerVtreeOps; + +typedef struct sdd_manager_t { + + SddSize id_counter; //used to generate new ids for nodes and elements + SddLiteral var_count; //number of variables in manager (vtree leaves) + + //counts + SddSize node_count; //dead + live + SddSize dead_node_count; + SddSize computed_count; + + //size of sdds + SddSize sdd_size; //dead + live + SddSize dead_sdd_size; //dead only + + //counts of free structures (available to be reclaimed) + SddSize gc_node_count; //number of free nodes + SddSize gc_element_count; //number of free elements + + //linked lists of free structures (linked through next) + SddNode** gc_node_lists; //buckets of linked lists + + struct vtree_t* vtree; + SddNode* true_sdd; + SddNode* false_sdd; + + //indexing literal sdds and leaf vtrees + struct sdd_node_t** literals; //array of literals + struct vtree_t** leaf_vtrees; //array of leaf vtrees + + //unique_nodes + SddHash* unique_nodes; + + //computation caches + SddSize computed_cache_lookup_count; + SddSize computed_cache_hit_count; + SddComputed* conjoin_cache; + SddComputed* disjoin_cache; + + //apply + SddLiteral apply_depth; //depth of apply call (1 means top-level apply) + SddLiteral limited_apply_depth; //depth of apply call with first limited ancestor having depth 1 + + //stacks + + //prime_sub stack for unique_node + SddElement* top_compression_stack; + SddElement* start_compression_stack; + SddSize capacity_compression_stack; + + //element stacks for cartesian products + SddElement* top_cp_stack1; + SddElement* start_cp_stack1; + SddSize capacity_cp_stack1; + + SddElement* top_cp_stack2; + SddElement* start_cp_stack2; + SddSize capacity_cp_stack2; + + SddElement* top_cp_stack3; + SddElement* start_cp_stack3; + SddSize capacity_cp_stack3; + + //stack for nesting compression stacks + SddSize* top_meta_compression_stack; //stack for storing pointers to prime_sub stack + SddSize* start_meta_compression_stack; + SddSize capacity_meta_compression_stack; + + //stack for compressed elements + SddElement* top_element_stack; + SddElement* start_element_stack; + SddNodeSize capacity_element_stack; + + //buffer for sorting nodes + SddNode** node_buffer; + SddSize node_buffer_size; + + //general options for manager + void* options; + + //manager stats + SddManagerStats stats; + + //options and stats for vtree operations + SddManagerVtreeOps vtree_ops; + + //automatic garbage collection and search + int auto_local_gc_and_search_on; //gc and search will be conducted locally only + int auto_gc_and_search_on; //automatic gc and search are possible + int auto_vtree_search_on; //whether we are currently searching for a vtree + Vtree* auto_apply_vtree; //vtree of top-level apply call + SddSize auto_apply_outside_live_size; //live size outside vtree of top-level apply call + SddSize auto_apply_outside_live_count; //live count outside vtree of top-level apply call + SddSize auto_apply_outside_dead_count; //dead count outside vtree of top-level apply call + int auto_gc_invocation_count; //number of auto gc's performed + int auto_search_invocation_count; //number of auto searches performed + int auto_search_invocation_count_global; //number of global auto searches performed + int auto_search_invocation_count_local; //number of local auto searches performed + int auto_search_invocation_count_recursive; //number of recursive auto searches performed + int auto_search_invocation_count_aborted_apply; //number of aborted auto searches (apply limits) + int auto_search_invocation_count_aborted_operation; //number of aborted auto searches (operation limits) + int auto_search_invocation_count_aborted_fragment; //number of aborted auto searches (fragment limits) + int auto_search_invocation_count_aborted_search; //number of aborted auto searches (search limits) + int auto_search_iteration_count; //number of iterations (passes) per search + float auto_search_reduction_sum; //sum of percentage size reduction over all searches + + //fragments + SddSize max_fragment_shadow_count; //maximum number of shadows constructed by any fragment + SddSize max_fragment_shadow_byte_count; //maximum number of shadows bytes constructed by any fragment + SddSize fragment_count; //number of fragments searched + SddSize completed_fragment_count; //number of fragments searched completely (all 12 states) + SddSize backward_completed_fragment_count; //number of fragments searched completely using backward search (all 12 states) + SddSize successful_fragment_count; //number of searched fragments leading to a better state + SddSize backward_successful_fragment_count; //number of backward searched fragments leading to a better state + SddSize successful_completed_fragment_count; //number of fragments searched completely and leading to a better state + + //the vtree search function to be used in auto vtree search + void* vtree_search_function; + +} SddManager; + + +/**************************************************************************************** + * function types + ****************************************************************************************/ + +//vtree search function +typedef struct vtree_t* SddVtreeSearchFunc(struct vtree_t*, struct sdd_manager_t*); + +/**************************************************************************************** + * WmcManager + * + * An environment for computing weighted model counts + * + ****************************************************************************************/ + +typedef struct wmc_manager_t { + int log_mode; + SddNode* node; //root of sdd + SddSize node_count; //number of nodes in sdd + SddNode** nodes; //sorted so children before parents + SddSize* node_indices; //indices of nodes in topologically sorted array + SddWmc* node_wmcs; + SddWmc* node_derivatives; + SddWmc* literal_weights; + SddWmc* literal_derivatives; + SddWmc* used_true_wmcs; + SddWmc* unused_true_wmcs; + SddWmc wmc; + SddManager* sdd_manager; +} WmcManager; + +/**************************************************************************************** + * SatManager + * + * An environment for deciding satisfiability + * + ****************************************************************************************/ + +typedef struct sat_manager_t { + SddLiteral var_count; //number of variables + SddSize node_count; //number of nodes in sdd + SddNode* node; //root of sdd + SddNode** nodes; //sorted so children before parents + SddSize* node_indices; //indices of nodes in topologically sorted array + int* node_sats; //holds 0 or 1 + char* var_values; //holds 'T', 'F', '?' + int sat; //0 or 1 + int needs_update; //0 or 1 +} SatManager; + +/**************************************************************************************** + * function prototypes + ****************************************************************************************/ + +// util.c +void press(char* str); +void header_strtok(char* buffer, const char* expected_token); +void test_and_exit(int test, const char* message); +void unexpected_node_type_error(char node_type); +int int_strtok(); +char char_strtok(); +char* read_file(const char* filename); +char* filter_comments(const char* buffer); +char* literal_to_label(SddLiteral lit); + +//verify.c +int verify_vtree_properties(const Vtree* vtree); +int verify_counts_and_sizes(const SddManager* manager); +int verify_normalization(const SddManager* manager); +int verify_negations(const SddManager* manager); +int verify_gc(const Vtree* vtree, SddManager* manager); +int verify_X_constrained(const Vtree* vtree); + +// +//basic +// + +//computed.c +SddNode* lookup_computation(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager); +void cache_computation(SddNode* node1, SddNode* node2, SddNode* node, BoolOp op, SddManager* manager); + +//gc.c +void sdd_vtree_garbage_collect(Vtree* vtree, SddManager* manager); + +//hash.c +#if defined(WIN32) || defined(_WIN32) +inline float hit_rate(SddHash* hash) { + return 100.0 * hash->hit_count / hash->lookup_count; +} + +inline float ave_lookup_cost(SddHash* hash) { + return ((float)hash->lookup_cost) / hash->lookup_count; +} +#else +float hit_rate(SddHash* hash); +float ave_lookup_cost(SddHash* hash); +#endif +float saturation(SddHash* hash); + +//reference.c +SddRefCount sdd_ref_count(SddNode* node); +SddNode* sdd_ref(SddNode* node, SddManager* manager); +SddNode* sdd_deref(SddNode* node, SddManager* manager); + +//sort.c +void sort_linked_nodes(SddSize count, SddNode** list, SddManager* manager); +void sort_uncompressed_elements(SddSize size, SddElement* elements); +void sort_compressed_elements(SddNodeSize size, SddElement* elements); +int elements_sorted_and_compressed(SddNodeSize size, SddElement* elements); + +// +//manager +// + +//manager.c +SddManager* sdd_manager_new(Vtree* vtree); +void sdd_manager_free(SddManager* manager); +void sdd_manager_print(SddManager* manager); + +//interface.c +void declare_interrupt_signal(); +void sdd_manager_auto_gc_and_minimize_on(SddManager* manager); +void sdd_manager_auto_gc_and_minimize_off(SddManager* manager); +int sdd_manager_is_auto_gc_and_minimize_on(SddManager* manager); +SddLiteral sdd_manager_var_count(SddManager* manager); +SddSize sdd_manager_size(const SddManager* manager); +SddSize sdd_manager_live_size(const SddManager* manager); +SddSize sdd_manager_dead_size(const SddManager* manager); +SddSize sdd_manager_count(const SddManager* manager); +SddSize sdd_manager_live_count(const SddManager* manager); +SddSize sdd_manager_dead_count(const SddManager* manager); +SddNode* sdd_manager_true(const SddManager* manager); +SddNode* sdd_manager_false(const SddManager* manager); +SddNode* sdd_manager_literal(const SddLiteral literal, const SddManager* manager); +Vtree* sdd_manager_vtree_copy(const SddManager* manager); +void sdd_manager_var_order(SddLiteral* var_order, SddManager* manager); +void sdd_manager_minimize(SddManager* manager); +void sdd_manager_minimize_limited(SddManager* manager); + +//variables.c +int sdd_manager_is_var_used(SddLiteral var, SddManager* manager); +int* var_usage_map(SddManager* manager); +void sdd_manager_add_var_before_first(SddManager* manager); +void sdd_manager_add_var_after_last(SddManager* manager); +void add_var_before_top(SddManager* manager); +void add_var_after_top(SddManager* manager); +void sdd_manager_add_var_before(SddLiteral target_var, SddManager* manager); +void sdd_manager_add_var_after(SddLiteral target_var, SddManager* manager); +void add_var_before_lca(int count, SddLiteral* variables, SddManager* manager); +void add_var_after_lca(int count, SddLiteral* variables, SddManager* manager); +void move_var_before_first(SddLiteral var, SddManager* manager); +void move_var_after_last(SddLiteral var, SddManager* manager); +void move_var_before(SddLiteral var, SddLiteral target_var, SddManager* manager); +void move_var_after(SddLiteral var, SddLiteral target_var, SddManager* manager); +void remove_var_added_last(SddManager* manager); + +// +//vtree_ops +// + +//dissect.c +Vtree* left_linearize_vtree(Vtree* vtree, SddManager* manager); +Vtree* right_linearize_vtree(Vtree* vtree, SddManager* manager); +Vtree* balance_vtree(Vtree* vtree, SddManager* manager); + +//move.c +void move_vtree_up_to(Vtree* vtree, Vtree** root_location, SddManager* manager); +void move_vtree_down(Vtree* vtree, SddManager* manager); + +//rotate_left.c +int sdd_vtree_rotate_left(Vtree* vtree, SddManager* manager, int limited); +//rotate_right.c +int sdd_vtree_rotate_right(Vtree* x, SddManager* manager, int limited); +//swap.c +int sdd_vtree_swap(Vtree* v, SddManager* manager, int limited); + +//fragment.c +VtreeFragment* vtree_fragment_new(Vtree* root, Vtree* child, SddManager* manager); +void vtree_fragment_free(VtreeFragment* fragment); +int vtree_fragment_is_initial(VtreeFragment* fragment); +Vtree* vtree_fragment_root(VtreeFragment* fragment); +int vtree_fragment_state(VtreeFragment* fragment); +int vtree_fragment_next(char direction, VtreeFragment* fragment, int limited); +Vtree* vtree_fragment_goto(int state, char direction, VtreeFragment* fragment); +Vtree* vtree_fragment_rewind(VtreeFragment* fragment); + +// +//vtree search +// + +//search.c +Vtree* sdd_vtree_minimize(Vtree* vtree, SddManager* manager); +Vtree* sdd_vtree_minimize_limited(Vtree* vtree, SddManager* manager); + +// +//fnf +// + +//compiler.c +SddNode* fnf_to_sdd(Fnf* fnf, SddManager* manager); + +//fnf.c +int is_cnf(Fnf* fnf); +int is_dnf(Fnf* fnf); +void free_fnf(Fnf* fnf); +int sdd_implies_cnf(SddNode* node, Cnf* cnf, SddManager* manager); +int dnf_implies_sdd(SddNode* node, Dnf* dnf, SddManager* manager); + +//io.c +Cnf* sdd_cnf_read(const char* filename); +Dnf* sdd_dnf_read(const char* filename); +void print_cnf(FILE* file, const Cnf* cnf); +void print_dnf(FILE* file, const Dnf* dnf); + +//vtree.c +void minimize_vtree_width(Fnf* fnf, Vtree** vtree_loc); + + +// +//sdd +// + +//apply.c +SddNode* sdd_apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager); +SddNode* sdd_negate(SddNode* node, SddManager* manager); + +//bits.c +void sdd_clear_node_bits(SddNode* node); +SddNode** sdd_topological_sort(SddNode* node, SddSize* size); +SddSize sdd_count_multiple_parent_nodes(SddNode* node); +SddSize sdd_count_multiple_parent_nodes_to_leaf(SddNode* node, Vtree* leaf); + +//cardinality.c +SddLiteral sdd_minimum_cardinality(SddNode* node); +SddNode* sdd_minimize_cardinality(SddNode* node, SddManager* manager); + +//condition.c +SddNode* sdd_condition(SddLiteral lit, SddNode* node, SddManager* manager); + +//copy.c +SddNode* sdd_copy(SddNode* node, SddManager* dest_manager); + +//exists.c +SddNode* sdd_exists(SddLiteral var, SddNode* node, SddManager* manager); + +//exists.c +SddNode* sdd_forall(SddLiteral var, SddNode* node, SddManager* manager); + +//io.c +void sdd_save_as_dot(const char* fname, SddNode *node); +void save_shared_sdd_as_dot_vt(const char* fname, Vtree* vtree); +void sdd_shared_save_as_dot(const char* fname, SddManager* manager); +void save_sdd_vt(const char* fname, SddNode *node, Vtree* vtree); +void sdd_save(const char* fname, SddNode *node); +SddNode* sdd_read(const char* filename, SddManager* manager); + +//model_count.c +SddModelCount sdd_model_count(SddNode* node, SddManager* manager); + +//node_count.c +SddSize sdd_count(SddNode* node); +SddSize sdd_all_node_count(SddNode* node); +SddSize sdd_all_node_count_leave_bits_1(SddNode* node); + +//rename_vars.c +SddNode* sdd_rename_variables(SddNode* node, SddLiteral* variable_map, SddManager* manager); + +//size.c +SddSize sdd_size(SddNode* node); +SddSize sdd_shared_size(SddNode** nodes, SddSize count); + +//essential_vars.c +int* sdd_variables(SddNode* node, SddManager* manager); +void set_sdd_variables(SddNode* node, SddManager* manager); + +//weighted_model_count.c +WmcManager* wmc_manager_new(SddNode* node, int log_mode, SddManager* manager); +void wmc_manager_free(WmcManager* wmc_manager); +SddWmc wmc_propagate(WmcManager* wmc_manager); +SddWmc wmc_zero_weight(WmcManager* wmc_manager); +SddWmc wmc_one_weight(WmcManager* wmc_manager); +void wmc_set_literal_weight(const SddLiteral literal, const SddWmc weight, WmcManager* wmc_manager); +SddWmc literal_weight(const SddLiteral literal, const WmcManager* wmc_manager); +SddWmc wmc_literal_derivative(const SddLiteral literal, const WmcManager* wmc_manager); +SddWmc wmc_literal_pr(const SddLiteral literal, const WmcManager* wmc_manager); + +// +//vtree +// + +//io.c +Vtree* sdd_vtree_read(const char* filename); +void sdd_vtree_save(const char* fname, Vtree* vtree); +void sdd_vtree_save_as_dot(const char* fname, Vtree* vtree); + +//compare.c +#if defined(WIN32) || defined(_WIN32) +//tests whether vtree1 is a subtree of vtree2 +inline int sdd_vtree_is_sub(const Vtree* vtree1, const Vtree* vtree2) { + return vtree1->position >= vtree2->first->position && vtree1->position <= vtree2->last->position; +} +#else +int sdd_vtree_is_sub(const Vtree* vtree1, const Vtree* vtree2); +#endif +Vtree* sdd_vtree_lca(Vtree* vtree1, Vtree* vtree2, Vtree* root); +Vtree* lca_of_compressed_elements(SddNodeSize size, SddElement* elements, SddManager* manager); + +//moves.c +int is_left_rotatable(Vtree* x); +int is_right_rotatable(Vtree* x); +void rotate_vtree_left(Vtree* x, SddManager* manager); +void rotate_vtree_right(Vtree* x, SddManager* manager); +void swap_vtree_children(Vtree* vtree, SddManager* manager); + +//static.c +Vtree* copy_vtree(Vtree* vtree); +Vtree* sdd_vtree_new(SddLiteral var_count, const char* type); +Vtree* sdd_vtree_new_with_var_order(SddLiteral var_count, SddLiteral* var_order, const char* type); + +//vtree.c +void sdd_vtree_free(Vtree* vtree); +Vtree* sdd_manager_vtree_of_var(const SddLiteral var, const SddManager* manager); +Vtree** sdd_vtree_location(Vtree* vtree, SddManager* manager); +Vtree* sibling(Vtree* vtree); + +//size.c +SddSize sdd_vtree_size(const Vtree* vtree); +SddSize sdd_vtree_live_size(const Vtree* vtree); +SddSize sdd_vtree_dead_size(const Vtree* vtree); +SddSize sdd_vtree_count(const Vtree* vtree); +SddSize sdd_vtree_live_count(const Vtree* vtree); +SddSize sdd_vtree_dead_count(const Vtree* vtree); +SddSize sdd_vtree_size_at(const Vtree* vtree); +SddSize sdd_vtree_live_size_at(const Vtree* vtree); +SddSize sdd_vtree_dead_size_at(const Vtree* vtree); +SddSize sdd_vtree_count_at(const Vtree* vtree); +SddSize sdd_vtree_live_count_at(const Vtree* vtree); +SddSize sdd_vtree_dead_count_at(const Vtree* vtree); +SddSize sdd_vtree_live_size_above(const Vtree* vtree); +SddSize sdd_vtree_dead_count_above(const Vtree* vtree); + +// +//search +// +Vtree* sdd_vtree_left(const Vtree* vtree); +Vtree* sdd_vtree_right(const Vtree* vtree); +Vtree* sdd_vtree_parent(const Vtree* vtree); +void sdd_vtree_set_search_state(void* search_state, Vtree* vtree); +void* sdd_vtree_search_state(const Vtree* vtree); +Vtree* sdd_manager_vtree(const SddManager* manager); +SddLiteral sdd_vtree_var_count(const Vtree* vtree); + +#endif // SDD_H_ + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/include/stacks.h b/pysdd/lib/libsdd-2.0/include/stacks.h new file mode 100644 index 0000000..db9a915 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/include/stacks.h @@ -0,0 +1,86 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#ifndef STACKS_H_ +#define STACKS_H_ + +/**************************************************************************************** + * stacks + * + * a stack named X is assumed to have three fields: + * + * -- X_start: a pointer to start of stack + * -- X_top: a pointer to top of stack (where next element will be added) + * -- X_capacity: number of elements stack can hold + * + ****************************************************************************************/ + +//check if stack size is equal to its capacity, doubling its capacity if this is the case +// +//T: type of elements on stack +//stack: name of stack +//manager: manager hosting stack +#define RESIZE_STACK_IF_FULL(T,stack,manager) {\ + if(manager->top_##stack == manager->start_##stack + manager->capacity_##stack) {\ + /* save relative location of current top */\ + SddSize size = manager->top_##stack - manager->start_##stack;\ + /* double capacity of stack */\ + manager->capacity_##stack *= 2;\ + /* reallocate stack with new capacity */\ + REALLOC(manager->start_##stack,T,manager->capacity_##stack,"stack");\ + /* reset top of new stack since realloc may have move the array */\ + manager->top_##stack = manager->start_##stack + size;\ + }\ +} + +//check whether stack is empty +#define IS_STACK_EMPTY(stack,manager) (manager->top_##stack == manager->start_##stack) +//number of elements currently on stack +#define STACK_SIZE(stack,manager) (manager->top_##stack - manager->start_##stack) +//start of stack +#define STACK_START(stack,manager) manager->start_##stack +//top of stack (where next element will go) +#define STACK_TOP(stack,manager) manager->top_##stack +//empty the stack (make top point to start of stack) +#define RESET_STACK(stack,manager) manager->top_##stack = manager->start_##stack +//remove and return element at top of stack (assumes stack is not empty) +#define POP_STACK(stack,manager) *(--manager->top_##stack) +//return (without removing) element at top of stack (assumes stack is not empty) +#define PEEK_STACK(stack,manager) *(manager->top_##stack-1) + +//add an element to the top of the stack +//E: element +//T: its type +#define PUSH_STACK(E,T,stack,manager) {\ + RESIZE_STACK_IF_FULL(T,stack,manager);\ + *(manager->top_##stack)++ = E;\ +} + +//the following is specific to element stacks + +#define POP_ELM(p,s,stack,manager)\ + SddElement* e = --manager->top_##stack;\ + p = e->prime;\ + s = e->sub;\ + +#define PUSH_ELM(p,s,stack,manager) {\ + RESIZE_STACK_IF_FULL(SddElement,stack,manager);\ + SddElement* e = (manager->top_##stack)++;\ + e->prime = p;\ + e->sub = s;\ +} + +#define SWITCH_ELM_STACKS(stack1,stack2,manager) {\ + SWAP(SddElement*,manager->start_##stack1,manager->start_##stack2);\ + SWAP(SddElement*,manager->top_##stack1,manager->top_##stack2);\ + SWAP(SddSize,manager->capacity_##stack1,manager->capacity_##stack2);\ +} + +#endif // STACKS_H_ + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/computed.c b/pysdd/lib/libsdd-2.0/src/basic/computed.c new file mode 100644 index 0000000..ec8b0d1 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/computed.c @@ -0,0 +1,91 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * computation cache + * + * there are two hash tables for caching computations, one for conjoin and one for disjoin + * + * there are no collision lists in these hash tables: just one entry (similar to CUDD) + * + * a cached computation may become invalid when one of its nodes is gc'd; such invalid + * entries are identified in a lazy manner. + * + * a node whose id is 0 must have been gc'd and is sitting on the gc list + * however, a node whose id is not 0 may have also been gc'd and its structure re-allocated + * + * ids of nodes are monotonically increasing: if a node is gc'd and then re-allocated, + * its new id will be larger than the old one. moreover, id=0 is reserved for nodes + * on the gc list. hence, if the id of a node is different than the one saved, then + * the node must have been gc'd + ****************************************************************************************/ + +static inline +void delete_computed(SddComputed* computed, SddManager* manager) { + --manager->computed_count; + computed->result = NULL; //computed now deleted +} + +static inline +SddSize hash_key(SddNode* node1, SddNode* node2) { + return ((16777619*node1->id)^(node2->id)) % COMPUTED_CACHE_SIZE; +} + +/**************************************************************************************** + * save + ****************************************************************************************/ + +void cache_computation(SddNode* node1, SddNode* node2, SddNode* node, BoolOp op, SddManager* manager) { + assert(!GC_NODE(node) && !GC_NODE(node1) && !GC_NODE(node2)); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + + if(node1->id > node2->id) SWAP(SddNode*,node1,node2); //for hash key + + SddSize key = hash_key(node1,node2); + SddComputed* table = op==CONJOIN? manager->conjoin_cache: manager->disjoin_cache; + SddComputed* computed = table+key; + + if(computed->result!=NULL) { //override current computed + delete_computed(computed,manager); + } + + ++manager->computed_count; + + computed->result = node; + computed->id = node->id; //needed to check whether result has been gc'd + computed->id1 = node1->id; //only id is needed for lookup + computed->id2 = node2->id; //only id is needed for lookup +} + +/**************************************************************************************** + * lookup + ****************************************************************************************/ + +SddNode* lookup_computation(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager) { + assert(!GC_NODE(node1) && !GC_NODE(node2)); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + + if(node1->id > node2->id) SWAP(SddNode*,node1,node2); //for hash key and ordered comparison + + SddSize key = hash_key(node1,node2); + SddComputed* table = op==CONJOIN? manager->conjoin_cache: manager->disjoin_cache; + SddComputed* computed = table+key; + + ++manager->computed_cache_lookup_count; + + if(computed->result==NULL || computed->id!=computed->result->id) return NULL; //missing or invalid computed + else if(computed->id1==node1->id && computed->id2==node2->id) { + ++manager->computed_cache_hit_count; //found it + return computed->result; //hit + } + else return NULL; //miss: non-matching computed for this key +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/count_and_size.c b/pysdd/lib/libsdd-2.0/src/basic/count_and_size.c new file mode 100644 index 0000000..6f83596 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/count_and_size.c @@ -0,0 +1,173 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/***************************************************************************************** + * node counts and sdd sizes apply only to sdd nodes that are in the unique table + * + * the following counts and sizes are maintained: + * + * manager: + * --node_count: total NUMBER of DEAD/ALIVE nodes in the manager + * --dead_node_count: total NUMBER of dead nodes in the manager + * --sdd_size: total SIZE of DEAD/ALIVE nodes (i.e., sum of their decomposition sizes) in the manager + * --dead_sdd_size: total SIZE of DEAD nodes in the manager + * + * vtree: + * --node_count: total NUMBER of DEAD/ALIVE nodes normalized for vtree + * --dead_node_count: total NUMBER of DEAD nodes that are normalized for vtree + * --sdd_size: total SIZE of DEAD/ALIVE nodes (i.e., sum of their decomposition sizes) normalized for vtree + * --dead_sdd_size: total SIZE of DEAD nodes that are normalized for vtree + * + * + * counts and sizes change (and require update) when a node: + * --is added to or removed from the unique table + * --changes its status between live and dead + * + * NOTE: during vtree operations, nodes may be removed temporarily from the unique + * table to be modified (rotated or swapped). until the vtree operation is concluded, + * and the nodes are put pack in the unique table, their counts and sizes will not + * be reflected by the counts and sizes stored at manager and vtree nodes + ****************************************************************************************/ + +/**************************************************************************************** + * sdd node sizes: at vtree nodes and for whole vtrees + ****************************************************************************************/ + +//at + +SddSize sdd_vtree_size_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->sdd_size; +} + +SddSize sdd_vtree_live_size_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->sdd_size-vtree->dead_sdd_size; +} + +SddSize sdd_vtree_dead_size_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->dead_sdd_size; +} + +//above + +//the size of all sdd nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_size_above(const Vtree* vtree) { + SddSize size = 0; + FOR_each_ancestral_vtree_node(v,vtree,size += v->sdd_size); + return size; +} + +//the size of all live sdd nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_live_size_above(const Vtree* vtree) { + SddSize size = 0; + FOR_each_ancestral_vtree_node(v,vtree,size += (v->sdd_size-v->dead_sdd_size)); + return size; +} + +//the size of all dead sdd nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_dead_size_above(const Vtree* vtree) { + SddSize size = 0; + FOR_each_ancestral_vtree_node(v,vtree,size += v->dead_sdd_size); + return size; +} + +//inside + +//the size of all sdd nodes that are normalized for vnodes in this vtree +SddSize sdd_vtree_size(const Vtree* vtree) { + SddSize size = 0; + FOR_each_internal_vtree_node(v,vtree,size += v->sdd_size); + return size; +} + +//the size of all live sdd nodes that are normalized for vnodes in this vtree +SddSize sdd_vtree_live_size(const Vtree* vtree) { + SddSize size = 0; + FOR_each_internal_vtree_node(v,vtree,size += (v->sdd_size-v->dead_sdd_size)); + return size; +} + +//the size of all dead sdd nodes that are normalized for vnodes in this vtree +SddSize sdd_vtree_dead_size(const Vtree* vtree) { + SddSize size = 0; + FOR_each_internal_vtree_node(v,vtree,size += v->dead_sdd_size); + return size; +} + +/**************************************************************************************** + * sdd node counts: at vtree nodes and for whole vtrees + ****************************************************************************************/ + +//at + +SddSize sdd_vtree_count_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->node_count; +} + +SddSize sdd_vtree_live_count_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->node_count-vtree->dead_node_count; +} + +SddSize sdd_vtree_dead_count_at(const Vtree* vtree) { + if(LEAF(vtree)) return 0; + else return vtree->dead_node_count; +} + +//above + +//number of decomposition nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_count_above(const Vtree* vtree) { + SddSize count = 0; + FOR_each_ancestral_vtree_node(v,vtree,count += v->node_count); + return count; +} + +//number of live decomposition nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_live_count_above(const Vtree* vtree) { + SddSize count = 0; + FOR_each_ancestral_vtree_node(v,vtree,count += (v->node_count-v->dead_node_count)); + return count; +} + +//number of dead decomposition nodes that are normalized for vnodes above this vtree +SddSize sdd_vtree_dead_count_above(const Vtree* vtree) { + SddSize count = 0; + FOR_each_ancestral_vtree_node(v,vtree,count += v->dead_node_count); + return count; +} + +//inside + +//number of decomposition nodes (live and dead) that are normalized for vnodes in this vtree +SddSize sdd_vtree_count(const Vtree* vtree) { + SddSize count = 0; + FOR_each_internal_vtree_node(v,vtree,count += v->node_count); + return count; +} + +//number of live decomposition nodes that are normalized for vnodes in this vtree +SddSize sdd_vtree_live_count(const Vtree* vtree) { + SddSize count = 0; + FOR_each_internal_vtree_node(v,vtree,count += (v->node_count-v->dead_node_count)); + return count; +} + +//number of dead decomposition nodes that are normalized for vnodes in this vtree +SddSize sdd_vtree_dead_count(const Vtree* vtree) { + SddSize count = 0; + FOR_each_internal_vtree_node(v,vtree,count += v->dead_node_count); + return count; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/gc.c b/pysdd/lib/libsdd-2.0/src/basic/gc.c new file mode 100644 index 0000000..4cfe0b8 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/gc.c @@ -0,0 +1,104 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/memory.c +void gc_sdd_node(SddNode* node, SddManager* manager); + +//basic/nodes.c +void remove_from_unique_table(SddNode* node, SddManager* manager); + +/**************************************************************************************** + * mark nodes that need to be gc'd + * --every dead node in vtree gets gc + * --dead nodes above vtree are gc'd iff they reference a node inside vtree (whether + * the reference is direct or indirect) + ****************************************************************************************/ + +static int is_gc(SddNode* node) { return node->git; } +static int is_dead(SddNode* node) { return node->ref_count==0; } + +//static SddSize all; +//static SddSize marked; + +//nodes above vtree must be visited bottom up +static +void mark_gc_nodes(Vtree* vtree) { + //mark nodes inside vtree for gc + FOR_each_internal_vtree_node(v,vtree,FOR_each_sdd_node_normalized_for(n,v,n->git=1)); + //mark selected nodes above vtree for gc + Vtree* ancestor = vtree->parent; + while(ancestor) { + int lchild = sdd_vtree_is_sub(vtree,ancestor->left); +// all += ancestor->dead_node_count; + if(ancestor->dead_node_count) { + FOR_each_sdd_node_normalized_for(n,ancestor,{ + if(n->ref_count==0) { + FOR_each_prime_sub_of_node(p,s,n,{ + n->git = lchild? p->git: s->git; +// if(n->git) ++marked; + if(n->git) break; //done with node n: it will be gc'd + }); + } + }); + } + ancestor = ancestor->parent; + } + FOR_each_internal_vtree_node(v,vtree,FOR_each_sdd_node_normalized_for(n,v,n->git=0)); +} + +/**************************************************************************************** + * garbage collection of decomposition sdd nodes: moving them to the gc list + ****************************************************************************************/ + +static inline +void garbage_collect_at(int test(SddNode*), Vtree* vtree, SddManager* manager) { + if(vtree->dead_node_count) { + FOR_each_sdd_node_normalized_for(n,vtree,{ + if(test(n)) { //gc node n + n->git = 0; + assert(n->parent_count==0); //node cannot have parents + remove_from_unique_table(n,manager); //first + gc_sdd_node(n,manager); //second + } + }); + } +} + +//visits ancestors vtree top-down: parents before children +static +void garbage_collect_above(Vtree* vtree, SddManager* manager) { + Vtree* root = manager->vtree; + while(root!=vtree && manager->dead_node_count) { + garbage_collect_at(is_gc,root,manager); +// garbage_collect_at(is_dead,root,manager); + if(sdd_vtree_is_sub(vtree,root->left)) root = root->left; + else root = root->right; + } +} + +//visits nodes inside vtree top-down: parents before children +void garbage_collect_in(Vtree* vtree, SddManager* manager) { + if(INTERNAL(vtree) && manager->dead_node_count) { + garbage_collect_at(is_dead,vtree,manager); + garbage_collect_in(vtree->left,manager); + garbage_collect_in(vtree->right,manager); + } +} + +//visit nodes top-down: when a node is gc'd, it must have no parents +void sdd_vtree_garbage_collect(Vtree* vtree, SddManager* manager) { + mark_gc_nodes(vtree); + garbage_collect_above(vtree,manager); + garbage_collect_in(vtree,manager); + assert(!FULL_DEBUG || verify_counts_and_sizes(manager)); + assert(!FULL_DEBUG || verify_gc(vtree,manager)); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/hash.c b/pysdd/lib/libsdd-2.0/src/basic/hash.c new file mode 100644 index 0000000..b49a010 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/hash.c @@ -0,0 +1,221 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +/******************************************************************************************** + * allowed sized of hash tables + *******************************************************************************************/ + +//the maximum qsize when resizing a hash table +#define MAX_QSIZE_SDD_NODE_HASH 21 + +//hash tables have sizes which are prime +//16 different sizes are predefined +//when a hash table is resized, the next size is used (roughly doubles the previous size) +//the largest hash table has about 10 million collision lists +static SddSize hash_qsizes[] = { + 317, + 631, + 1259, + 2503, + 5003, + 10007, + 20001, + 40009, + 80021, + 160001, + 320009, + 640007, + 1280023, + 2560021, + 5000011, + 10000019, + 20000003, + 40000003, + 80000023, + 160000003, + 320000077, + 640000019 +}; + +/******************************************************************************************** + * creating hash tables + *******************************************************************************************/ + +SddHash* new_unique_node_hash(SddManager* manager) { + //convert qualitative size to numeric size + SddSize size = hash_qsizes[INITIAL_SIZE_UNIQUE_NODE_TABLE]; + + SddHash* hash; + MALLOC(hash,SddHash,"NEW_HASH"); + //allocate array of collision lists + //array must be initialized to null pointers: empty collision lists + CALLOC(hash->clists,SddNode*,size,"NEW_HASH"); + + hash->size = size; + hash->qsize = INITIAL_SIZE_UNIQUE_NODE_TABLE; + hash->count = 0; + hash->lookup_count = 0; + hash->hit_count = 0; + hash->resize_age = 0; + hash->lookup_cost = 0; + hash->increase_size_count = 0; + hash->decrease_size_count = 0; + + return hash; +} + +/******************************************************************************************** + * freeing hash tables + *******************************************************************************************/ + +void free_hash(SddHash* hash) { + free(hash->clists); + free(hash); +} + +/******************************************************************************************** + * hash keys + *******************************************************************************************/ + +//hash key of an sdd node that has not been constructed yet +static inline +SddSize key_sdd_elements(SddSize size, SddElement* elements, SddHash* hash) { + SddSize key = 0; + FOR_each_prime_sub_of_elements(p,s,size,elements,{ + key += (16777619*key)^(p->id); + key += (16777619*key)^(s->id); + }); + return key % hash->size; +} + +//hash key for existing node +static inline +SddSize key_sdd_node(SddNode* node, SddHash* hash) { + return key_sdd_elements(node->size,ELEMENTS_OF(node),hash); +} + +/******************************************************************************************** + * hash table properties + *******************************************************************************************/ + +#if defined(WIN32) || defined(_WIN32) +#else +inline +float hit_rate(SddHash* hash) { + return 100.0*hash->hit_count/hash->lookup_count; +} + +inline +float ave_lookup_cost(SddHash* hash) { + return ((float)hash->lookup_cost)/hash->lookup_count; +} +#endif + + +//percentage of entries in hash table that have non-empty collision lists +float saturation(SddHash* hash) { + SddSize count = 0; + for(SddNode** clist=hash->clists; clist < hash->clists+hash->size; ++clist) { + if(*clist) ++count; + } + return ((float)count)/hash->size; +} + +/******************************************************************************************** + * resizing hash table + *******************************************************************************************/ + +//resizing up or down depends on hash table load +//keeps contents as is +void try_resizing_hash(SddHash* hash, SddManager* manager) { + + if(hash->qsize != MAX_QSIZE_SDD_NODE_HASH && hash->count > LOAD_TO_INCREASE_HASH_SIZE*hash->size) { + ++hash->qsize; //increase size + ++hash->increase_size_count; + } + else if(hash->qsize != 0 && hash->count < LOAD_TO_DECREASE_HASH_SIZE*hash->size) { + --hash->qsize; //decrease size + ++hash->decrease_size_count; + } + else return; //no resizing + + //save old table + SddSize old_size = hash->size; + SddNode** old_clists = hash->clists; + + //new array of collision lists + hash->size = hash_qsizes[hash->qsize]; + CALLOC(hash->clists,SddNode*,hash->size,"resize_sdd_node_hash"); + + //insert nodes into new array of collision lists (using new hash keys) + for(SddNode** old_clist=old_clists; old_clistclists+key_sdd_node(node,hash); //location of new collision list + if(*new_clist) (*new_clist)->prev = &(node->next); + node->next = *new_clist; + node->prev = new_clist; + *new_clist = node; + }); + } + + free(old_clists); //free old table + hash->resize_age = 0; //reset resize age +} + +/******************************************************************************************** + * looking up entries in hash tables + *******************************************************************************************/ + +//assumes elements are sorted +SddNode* lookup_sdd_node(SddElement* elements, SddNodeSize size, SddHash* hash, SddManager* manager) { + ++hash->lookup_count; + ++hash->resize_age; + //search + SddNode* node = *(hash->clists+key_sdd_elements(size,elements,hash)); //collision list + SddSize s = size*sizeof(SddElement); + while(node) { + ++hash->lookup_cost; + if(node->size==size && memcmp(ELEMENTS_OF(node),elements,s)==0) { //found it + ++hash->hit_count; + return node; + } + else node=node->next; + } + return NULL; //did not find it +} + +/******************************************************************************************** + * inserting entries in hash table + *******************************************************************************************/ + +//insert node into hash table +void insert_sdd_node(SddNode* node, SddHash* hash, SddManager* manager) { + ++hash->count; + SddNode** clist = hash->clists+key_sdd_node(node,hash); //collision list location + if(*clist) (*clist)->prev = &(node->next); + node->next = *clist; + node->prev = clist; + *clist = node; + //resizing is tried only after adding node to hash table (and not after removing nodes) + if(hash->resize_age > AGE_BEFORE_HASH_RESIZE_CHECK) try_resizing_hash(hash,manager); +} + +/******************************************************************************************** + * removing entries in hash table + *******************************************************************************************/ + +void remove_sdd_node(SddNode* node, SddHash* hash, SddManager* manager) { + --hash->count; + if(node->next) node->next->prev = node->prev; + *(node->prev) = node->next; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/memory.c b/pysdd/lib/libsdd-2.0/src/basic/memory.c new file mode 100644 index 0000000..d213cf6 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/memory.c @@ -0,0 +1,180 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +//basic/nodes.c +void declare_lost_parent(SddNode* node, SddManager* manager); + +/***************************************************************************************** + * the following are critical assumptions about ids of nodes: + * + * --id 0 is reserved for nodes on the gc list + * --id 1 is reserved for false + * --id 2 is reserved for true + * --id of true = (id of false) + 1 (see basic/compress.c) + * --ids are NEVER re-used: every newly created sdd node (by malloc or recovery from the + * list) gets a new id; hence, the ids of nodes returned by new_sdd_node() are + * monotonically increasing (see the definition of macro INVALID_COMPUTED) + ****************************************************************************************/ + +/***************************************************************************************** + * nodes are either: + * --ALLOCATED: terminal sdd nodes, or nodes indexed by a unique table + * --GC'D: part of the gc list + * + * allocated decomposition nodes are either: + * --DEAD: have reference count == 0 (ready to be garbage collected) + * --LIVE: have reference count > 0 + * + ****************************************************************************************/ + +/**************************************************************************************** + * allocating nodes + * first, allocation is attempted from gc lists + * otherwise, malloc is called + * + * elements are different though: rotation and swap replace the elements of a node, + * so they create and free elements which are not accounted for by manager->sdd_size + * and manager->gc_element_count). hence, one must explicitly keep track of the + * maximum number of elements existing in memory. + ****************************************************************************************/ + +/**************************************************************************************** + * allocating and freeing sdd elements + ****************************************************************************************/ + +//allocate an array to hold elements +SddElement* new_elements(SddNodeSize size, SddManager* manager) { + assert(size>0); + SddElement* elements; + CALLOC(elements,SddElement,size,"new_element_array"); + manager->stats.element_count += size; //number of elements currently in memory + manager->stats.max_element_count = MAX(manager->stats.max_element_count,manager->stats.element_count); + return elements; +} + +void free_elements(SddNodeSize size, SddElement* elements, SddManager* manager) { + assert(size>0 || elements==NULL); + assert(manager->stats.element_count >= size); + free(elements); + manager->stats.element_count -= size; //number of elements currently in memory + assert(manager->stats.max_element_count >= manager->stats.element_count); +} + +/**************************************************************************************** + * allocating sdd nodes + ****************************************************************************************/ + +//gc'd nodes are kept in linked lists, depending on their size +//if size is >0 and = GC_BUCKETS_COUNT, then the node is stored in gc_node_lists[0] +//allocate from gc list if non empty, otherwise malloc +//new node is normalized for vtree +SddNode* new_sdd_node(SddNodeType type, SddNodeSize size, Vtree* vtree, SddManager* manager) { + + SddNode* node; + + if(type!=DECOMPOSITION) { //allocate terminal node + MALLOC(node,SddNode,"new_sdd_node"); + } + else { //allocate decomposition + assert(size > 0); + //allocate decomposition node + size_t bucket = (size < GC_BUCKETS_COUNT? size: 0); + node = manager->gc_node_lists[bucket]; + if(node != NULL) { //allocating from gc list + --manager->gc_node_count; + manager->gc_element_count -= node->size; //0 for bucket 0 + manager->gc_node_lists[bucket] = node->next; //remove node from gc list + if(bucket==0) ELEMENTS_OF(node) = new_elements(size,manager); + //nodes in bucket zero have no elements allocated (since these nodes + //were of varying size when they were put there, their elements were freed) + } + else { //allocating new memory + MALLOC(node,SddNode,"new_sdd_node"); + ELEMENTS_OF(node) = new_elements(size,manager); + } + } + + //initialize + node->id = ++manager->id_counter; + node->vtree = vtree; //node is normalized for vtree + node->type = type; + node->size = size; + node->negation = NULL; + node->multiply_sub = NULL; + node->map = NULL; + node->shadow = NULL; + node->saved_elements = NULL; + node->saved_size = 0; + node->ref_count = 0; //nodes are born dead + node->parent_count = 0; + node->index = 0; + node->bit = 0; + node->cit = 0; + node->dit = 0; + node->git = 0; + node->in_unique_table = 0; + node->replaced = 0; + node->user_bit = 0; + + return node; +} + + +/**************************************************************************************** + * garbage collecting (freeing) sdd nodes + ****************************************************************************************/ + +//gc'd nodes are kept in linked lists, depending on their size +//if size is >=0 and = GC_BUCKETS_COUNT, then the node is stored in gc_node_lists[0] +// +//note: literal sdds are not garbage collected except when their corresponding variable +//is removed from the manager by remove_var(). +// +//literal sdds go into gc_node_lists[0] +// +void gc_sdd_node(SddNode* node, SddManager* manager) { + assert(node->parent_count==0); //nothing should be pointing to node + assert(NON_TRIVIAL(node)); //only decomposition and literal nodes + + //remove reference by negation reference (if any) + if(node->negation!=NULL) node->negation->negation=NULL; + + if(IS_DECOMPOSITION(node)) { + assert(node->ref_count==0); //must be dead + assert(node->in_unique_table==0); //cannot be in unique table + declare_lost_parent(node,manager); //before gc which may free elements + } + + //add to the corresponding free list + ++manager->gc_node_count; + manager->gc_element_count += node->size; //no change for literal sdds + + size_t bucket = (node->size < GC_BUCKETS_COUNT? node->size: 0); //literal sdds go into bucket 0 + node->next = manager->gc_node_lists[bucket]; + manager->gc_node_lists[bucket] = node; //add node to corresponding bucket + + if(bucket==0 && IS_DECOMPOSITION(node)) { + //nodes put in this bucket have varying size so their elements cannot be easily reused + //we will therefore free these elements + manager->gc_element_count -= node->size; //correction: these elements are not on gc-list + free_elements(node->size,ELEMENTS_OF(node),manager); + node->size = 0; + ELEMENTS_OF(node) = NULL; //so that free(ELEMENTS_OF(node)) is still ok + } + + node->id = 0; //node is now garbage + + //note: size and element array are kept intact except for bucket 0 +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/multiply.c b/pysdd/lib/libsdd-2.0/src/basic/multiply.c new file mode 100644 index 0000000..c594974 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/multiply.c @@ -0,0 +1,209 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/apply.c +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited); + +/**************************************************************************************** + * multiplying two decompositions + * + * if 1st decomposition has elements pi.si and 2nd decomposition has elements pj.sj, + * then define prime P = pi CONJOIN pj, sub S = si OP si, and + * call code fn for each P.S, where prime P is not false + * + * when computing a product of two decompositions: (p11 p12 ... p1n) x (p21 p22 ... p2m) + * --observation 1: + * if p1i = p2j, then p1k x p2j = false for all k<>i + * --observation 2: + * if p1i = !p2j, then p1k x p2j = p1k for all k<>i + * --observation 3: + * if p1i*p2j=p2j, then pik x p2j = false for all k<>i + * + * the above observations are used to skip some conjoin operations whose result + * can be predicted upfront + ****************************************************************************************/ + +//multiplies decompositions elements1/size1 and elements2/size2 +//(p1,s1)*(p2,s2) is defined as (p1 and p2,s1 op s2) when p1 and p2 is consistent +//calls function fn on each element of the product + +#define SIZE_THR 1024 + +//returns 1 if time limit is not exceeded, 0 otherwise +// +//assumes that fn has no sides effets that need to be undone upon failure due to time out +//assumes that either: +// --elements1/2 are referenced and fn will end up referencing its prime and sub arguments, or +// --calls to apply inside multiply_decompositions will not invoke minimization/gc +// +//multiply_decompositions is only called in two places, which both satisfy the above assumptions +// +//note: since this is called by multiply cartesian products, it is possible that size1=1 or size2=1 +// +int multiply_decompositions( + + SddElement* elements1, SddNodeSize size1, + SddElement* elements2, SddNodeSize size2, + BoolOp op, Vtree* vtree, SddManager* manager, int limited, + void fn(SddNode* prime, SddNode* sub, Vtree*, SddManager* manager)) { + + //INITIALIZE + + SddElement E1[SIZE_THR]; //allocated on stack + SddElement E2[SIZE_THR]; //allocated on stack + SddElement* e1 = E1; + SddElement* e2 = E2; + SddNode* MS1[SIZE_THR]; //allocated on stack + SddNode* MS2[SIZE_THR]; //allocated on stack + SddNode** ms1 = MS1; + SddNode** ms2 = MS2; + //when size1 or size2 are large enough, allocate on heap to avoid stack overflow + if(size1 > SIZE_THR) CALLOC(e1,SddElement,size1,"multiply_decompositions"); //on heap + if(size2 > SIZE_THR) CALLOC(e2,SddElement,size2,"multiply_decompositions"); //on heap + if(size1 > SIZE_THR) CALLOC(ms1,SddNode*,size1,"multiply_decompositions"); //on heap + if(size2 > SIZE_THR) CALLOC(ms2,SddNode*,size2,"multiply_decompositions"); //on heap + SddElement* e1_start = e1; + SddElement* e2_start = e2; + SddElement* e1_end = e1+size1; + SddElement* e2_end = e2+size2; + SddNode* p1 = NULL; + SddNode* p2 = NULL; + SddNode* s1 = NULL; + SddNode* s2 = NULL; + + //PREPROCESS ELEMENTS: identify opportunities for avoiding a full quadratic complexity + + //mark primes of elements1 + for(SddElement* i=elements1; iprime->dit = 1; + + //copy elements2 to e2: elements with common primes appearing first in e2 + for(SddElement* i=elements2; iprime->dit = !(i->prime->dit); + if(i->prime->dit==0) *e2_start++ = *i; else *(--e2_end) = *i; + if(i->prime->negation!=NULL && i->prime->negation->dit==1) { p2 = i->prime; s2 = i->sub; } + } + + //copy elements1 to e1: elements with common primes appearing first in e1 + for(SddElement* i=elements1; iprime->dit==0) *e1_start++ = *i; else *(--e1_end) = *i; + i->prime->dit = 0; + if(p2!=NULL && i->prime==p2->negation) { p1 = i->prime; s1 = i->sub; } + } + //primes of elements1 are now unmarked + + // unmark primes of elements2 + for(SddElement* i=elements2; iprime->dit = 0; + + //MULTIPLY + + if(p1==NULL) goto compute_product; + + //p1 and p2 are complementary: p1 in e1 and p2 in e2 + //multiply has LINEAR complexity in this case + assert(p2!=NULL); + + //iterate over elements of e1 except for p1 + for(SddElement* i=e1; iprime==p1) continue; + SddNode* prime = i->prime; + SddNode* sub = apply(i->sub,s2,op,manager,limited); + if(sub==NULL) goto failure; //exceeded time limit + fn(prime,sub,vtree,manager); //execute code + } + + //iterate over elements of e2 except for p2 + for(SddElement* j=e2; jprime==p2) continue; + SddNode* prime = j->prime; + SddNode* sub = apply(j->sub,s1,op,manager,limited); + if(sub==NULL) goto failure; //exceeded time limit + fn(prime,sub,vtree,manager); //execute code + } + + goto success; //done + + compute_product: + //must compute products + + //A1: multiply common elements (usually, this leads to more added elements than B) + //the following fragment is quadratic in size1 and size2 (more efficient than A2 when the product is small enough) + if((e1_end-e1)*(e2_end-e2) <= 64) { + e2_start = e2; // so it can be changed + for(SddElement* i=e1; iprime==j->prime) { + SddNode* prime = i->prime; + SddNode* sub = apply(i->sub,j->sub,op,manager,limited); + if(sub==NULL) goto failure; //exceeded time limit + fn(prime,sub,vtree,manager); //execute code + *j = *e2_start++; // exclude *j from further iterations + break; + } + } + } + } + //A2: multiply common elements (usually, this leads to more added elements than B) + //the following fragment is linear in size1 and size2 + else { + //save the multiply subs of e1 and e2 + for(SddElement* i=e1; iprime->multiply_sub; + for(SddElement* i=e2; iprime->multiply_sub; + //clear the multiply subs of e1 + for(SddElement* i=e1; iprime->multiply_sub = NULL; + //set the multiply subs of e2 + for(SddElement* i=e2; iprime->multiply_sub = i->sub; + //identify elements of e1 that are common with e2 (have the same prime) + for(SddElement* i=e1; iprime->multiply_sub!=NULL) { + SddNode* prime = i->prime; //an element of e2 has the same prime + SddNode* sub = apply(i->sub,prime->multiply_sub,op,manager,limited); + if(sub==NULL) { //exceeded time limit + //recover the multiply subs of e1 and e2 + for(SddElement* i=e1; iprime->multiply_sub = ms1[i-e1]; + for(SddElement* i=e2; iprime->multiply_sub = ms2[i-e2]; + goto failure; + } + fn(prime,sub,vtree,manager); //execute code + } + } + //recover the multiply subs of e1 and e2 + for(SddElement* i=e1; iprime->multiply_sub = ms1[i-e1]; + for(SddElement* i=e2; iprime->multiply_sub = ms2[i-e2]; + } + + //B: multiply non-common elements (usually, this leads to smaller added elements than A) + + for(SddElement* i=e1_end; iprime,j->prime,CONJOIN,manager,limited); + if(prime==NULL) goto failure; //exceeded time limit + if(!IS_FALSE(prime)) { + SddNode* sub = apply(i->sub,j->sub,op,manager,limited); + if(sub==NULL) goto failure; //exceeded time limit + fn(prime,sub,vtree,manager); //execute code + } + if(prime==i->prime) break; + if(prime==j->prime) *j = *e2_end++; /* exclude *j from further iterations */ + } + } + + success: + if(size1 > SIZE_THR) { free(e1); free(ms1); } + if(size2 > SIZE_THR) { free(e2); free(ms2); } + return 1; + + failure: + if(size1 > SIZE_THR) { free(e1); free(ms1); } + if(size2 > SIZE_THR) { free(e2); free(ms2); } + return 0; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/nodes.c b/pysdd/lib/libsdd-2.0/src/basic/nodes.c new file mode 100644 index 0000000..02ca9ce --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/nodes.c @@ -0,0 +1,174 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/memory.c +SddNode* new_sdd_node(SddNodeType type, SddNodeSize size, Vtree* vtree, SddManager* manager); +void free_elements(SddNodeSize size, SddElement* elements, SddManager* manager); + +//basic/hash.c +void insert_sdd_node(SddNode* node, SddHash* hash, SddManager* manager); +void remove_sdd_node(SddNode* node, SddHash* hash, SddManager* manager); + +/**************************************************************************************** + * freeing an sdd node structure + ****************************************************************************************/ + +void free_sdd_node(SddNode* node, SddManager* manager) { + if(node->type==DECOMPOSITION) { + free_elements(node->size,ELEMENTS_OF(node),manager); + free(node); + } + else free(node); //terminal sdd node +} + +/**************************************************************************************** + * updating counts and sizes + ****************************************************************************************/ + +//node was just added or removed from unique table +static inline +void update_counts_and_sizes_after_unique_table_change(SddNode* node, SddManager* manager) { + assert(node->type==DECOMPOSITION); + + int inc = node->in_unique_table? 1: -1; + //added node: inc= +1 + //removed node: inc= -1 + + Vtree* vtree = node->vtree; + SddSize size = node->size; + + //total counts and sizes + manager->node_count += inc; + manager->sdd_size += inc*size; + vtree->node_count += inc; + vtree->sdd_size += inc*size; + + //dead counts and sizes + //live sizes are not maintained: computed from total/dead + if(node->ref_count==0) { //dead node + manager->dead_node_count += inc; + manager->dead_sdd_size += inc*size; + vtree->dead_node_count += inc; + vtree->dead_sdd_size += inc*size; + } +} + +/**************************************************************************************** + * maintaining parent counts for sdd nodes + * + * sdd node n is a parent of sdd node m iff m is a prime or sub of node n + ****************************************************************************************/ + +void declare_acquired_parent(SddNode* node, SddManager* manager) { + assert(IS_DECOMPOSITION(node)); + FOR_each_prime_sub_of_node(prime,sub,node,{ + ++prime->parent_count; + ++sub->parent_count; + }); +} + +void declare_lost_parent(SddNode* node, SddManager* manager) { + assert(IS_DECOMPOSITION(node)); + FOR_each_prime_sub_of_node(prime,sub,node,{ + assert(prime->parent_count && sub->parent_count); + --prime->parent_count; + --sub->parent_count; + }); +} + +/**************************************************************************************** + * inserting and removing sdd nodes from the unique table + ****************************************************************************************/ + +void insert_in_unique_table(SddNode* node, SddManager* manager) { + assert(node->type==DECOMPOSITION); + assert(node->in_unique_table==0); + + //insert in hash table + insert_sdd_node(node,manager->unique_nodes,manager); + //insert in nodes of vtree + Vtree* vtree = node->vtree; + if(vtree->nodes) vtree->nodes->vtree_prev = &(node->vtree_next); + node->vtree_next = vtree->nodes; + node->vtree_prev = &(vtree->nodes); + vtree->nodes = node; + //update counts and sizes + node->in_unique_table = 1; //before updating counts and sizes + update_counts_and_sizes_after_unique_table_change(node,manager); +} + +void remove_from_unique_table(SddNode* node, SddManager* manager) { + assert(node->type==DECOMPOSITION); + assert(node->in_unique_table==1); + + //remove from hash table + remove_sdd_node(node,manager->unique_nodes,manager); + //remove from nodes of vtree + if(node->vtree_next) node->vtree_next->vtree_prev = node->vtree_prev; + *(node->vtree_prev) = node->vtree_next; + //update counts and sizes + node->in_unique_table = 0; //before updating counts and sizes + update_counts_and_sizes_after_unique_table_change(node,manager); +} + +/**************************************************************************************** + * constructing decomposition sdd nodes + ****************************************************************************************/ + +//the elements array is not used by the newly constructed node: new array is created +SddNode* construct_decomposition_sdd_node(SddNodeSize size, SddElement* elements, Vtree* vtree, SddManager* manager) { + assert(elements_sorted_and_compressed(size,elements)); + assert(vtree==lca_of_compressed_elements(size,elements,manager)); + + //allocate node (and its elements) + SddNode* node = new_sdd_node(DECOMPOSITION,size,vtree,manager); + //copy elements into node + memcpy(ELEMENTS_OF(node),elements,size*sizeof(SddElement)); + //insert in unique table + insert_in_unique_table(node,manager); + //primes and subs of node have acquired a new parent + declare_acquired_parent(node,manager); + + return node; +} + + +/**************************************************************************************** + * constructing literal sdd nodes + * + * there are two literal sdds for each variable + ****************************************************************************************/ + +//this is called only when constructing manager +SddNode* construct_literal_sdd_node(SddLiteral literal, Vtree* vtree, SddManager* manager) { + SddNode* node = new_sdd_node(LITERAL,0,vtree,manager); + LITERAL_OF(node) = literal; + return node; +} + +/**************************************************************************************** + * constructing true and false sdd nodes for manager + * + * there is one true sdd and one false sdd per manager + ****************************************************************************************/ + +//this is called only when constructing manager +SddNode* construct_true_sdd_node(SddManager* manager) { + SddNode* node = new_sdd_node(TRUE,0,NULL,manager); + return node; +} + +//this is called only when constructing manager +SddNode* construct_false_sdd_node(SddManager* manager) { + SddNode* node = new_sdd_node(FALSE,0,NULL,manager); + return node; +} + +/***************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/partitions.c b/pysdd/lib/libsdd-2.0/src/basic/partitions.c new file mode 100644 index 0000000..95d7c43 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/partitions.c @@ -0,0 +1,351 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/hash.c +SddNode* lookup_sdd_node(SddElement* elements, SddNodeSize size, SddHash* hash, SddManager* manager); + +//basic/memory.c +SddElement* new_elements(SddNodeSize size, SddManager* manager); + +//basic/nodes.c +SddNode* construct_decomposition_sdd_node(SddNodeSize size, SddElement* elements, Vtree* vtree, SddManager* manager); + +//sdds/apply.c +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited); + +/**************************************************************************************** + * a utility for constructing a compressed, trimmed partition from an uncompressed one: + * + * --START_partition: is called before uncompressed elements are declared + * + * --DECLARE_element: is called to declare elements + * + * --GET_node_of_partition: is called after all elements has been declared + * and returns an sdd node corresponding to the compressed and trimmed partition + * + * --GET_elements_of_partition: is called after all elements has been + * declared returns the compressed elements (assumes no trimming is possible). + * + * the macro GET_node_from_partition provides a shortcut for this usage, returning + * a unique node corresponding to the compressed and trimmed elements + * + * the above utility in this file is also used for constructing cartesian products + * + * one can also get nodes corresponding to a compressed partition using START_partition(), + * DECLARE_compressed_element() and GET_node_of_compressed_partition() + * the macro GET_node_from_compressed_partition provides a shortcut for this + ****************************************************************************************/ + +/**************************************************************************************** + * macros for popping/pushing elements from compression and elements stack, while + * ensuring protection of elements + * + * protection is needed in case compression invokes vtree search + ****************************************************************************************/ + +//reference primes/subs when pushing elements on compression_stack +//dereference primes/subs when popping elements from compression_stack +#define uPUSH_ELM(P,S,M) { PUSH_ELM(P,S,compression_stack,M); if(M->auto_gc_and_search_on) { sdd_ref(P,M); sdd_ref(S,M); } } +#define uPOP_ELM(P,S,M) { POP_ELM(P,S,compression_stack,M); if(M->auto_gc_and_search_on) { sdd_deref(P,M); sdd_deref(S,M); } } +//#define uPUSH_ELM(P,S,M) { PUSH_ELM(P,S,compression_stack,M); } +//#define uPOP_ELM(P,S,M) { POP_ELM(P,S,compression_stack,M); } + +//reference primes when pushing elements on element_stack +//dereference primes when popping elements from element_stack +//NOTE: compression will not gc subs +#define cPUSH_ELM(P,S,M) { PUSH_ELM(P,S,element_stack,M); if(M->auto_gc_and_search_on) sdd_ref(P,M); } +#define cPOP_ELM(P,S,M) { POP_ELM(P,S,element_stack,M); if(M->auto_gc_and_search_on) sdd_deref(P,M); } +//#define cPUSH_ELM(P,S,M) { PUSH_ELM(P,S,element_stack,M); } +//#define cPOP_ELM(P,S,M) { POP_ELM(P,S,element_stack,M); } + +/**************************************************************************************** + * openning a partition + ****************************************************************************************/ + +//saves number of elements currently on compression_stack +void START_partition(SddManager* manager) { + SddSize cur_element_count = manager->top_compression_stack - manager->start_compression_stack; + PUSH_STACK(cur_element_count,SddSize,meta_compression_stack,manager); +} + +/**************************************************************************************** + * declaring elements + ****************************************************************************************/ + +//push element on compression stack +//used for declaring elements that may need to be compressed +void DECLARE_element(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager *manager) { + assert(!IS_FALSE(prime)); + assert(IS_TRUE(prime) || sdd_vtree_is_sub(prime->vtree,vtree->left)); + assert(TRIVIAL(sub) || sdd_vtree_is_sub(sub->vtree,vtree->right)); + + uPUSH_ELM(prime,sub,manager); +} + +//push element on compression stack +//used for declaring elements that are compressed +void DECLARE_compressed_element(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager *manager) { + assert(!IS_FALSE(prime)); + assert(IS_TRUE(prime) || sdd_vtree_is_sub(prime->vtree,vtree->left)); + assert(TRIVIAL(sub) || sdd_vtree_is_sub(sub->vtree,vtree->right)); + + PUSH_ELM(prime,sub,compression_stack,manager); //no referencing +} + +/**************************************************************************************** + * aborting a node partition + ****************************************************************************************/ + +//this is called when limits are exceeded before the partition is closed +//it removes the elements declared thus far from compression_stack +void ABORT_partition(SddManager *manager) { + + SddSize prev_elem_count = POP_STACK(meta_compression_stack,manager); + SddSize total_elem_count = manager->top_compression_stack - manager->start_compression_stack; + SddSize cur_elm_count = total_elem_count-prev_elem_count; + + SddNode* p; SddNode* s; + while(cur_elm_count--) { uPOP_ELM(p,s,manager); } +} + +/**************************************************************************************** + * reseting compression_stack and element_stack to their initial state (before + * a partition was opened). + ****************************************************************************************/ + +//compressed_count: number of elements pushed on the elements stack (by compression) +//uncompressed_count: number of elements put on the compression stack (by declare element) +//processed_uncompressed_count: number of elements poped from the compression stack +// +//Note: popping does not really remove elements from the stack; it just adjusts the +//pointer to the stack top (elements continue to reside on the stack even after popping) +static inline +void reset_stacks(SddSize compressed_count, SddSize uncompressed_count, SddSize processed_uncompressed_count, SddManager* manager) { + SddNode* p; SddNode* s; + + //reset compressed elements stack + while(compressed_count--) { cPOP_ELM(p,s,manager); } + + //reset uncompressed elements stack + while(processed_uncompressed_count++ < uncompressed_count) { uPOP_ELM(p,s,manager); } +} + +/**************************************************************************************** + * compressing and trimming declared elements of partition + ****************************************************************************************/ + +//if node!=NULL is returned, then trimming took place and node is the result +//if node==NULL, then size and elements describe the compressed elements +static +int compress_and_trim(SddNodeSize* size, SddElement** elements, SddNode** node, Vtree* vtree, SddManager* manager, int limited) { + + //count of elements on the compression stack before this partition was opened + SddSize prev_elements_count = POP_STACK(meta_compression_stack,manager); + //count and array of elements to be compressed + SddElement* uncompressed_elements = manager->start_compression_stack + prev_elements_count; + SddSize uncompressed_count = manager->top_compression_stack - uncompressed_elements; + + //at least one element must have been declared + assert(uncompressed_count >= 1); + + //keeping track of largest uncompressed decomposition + manager->stats.max_uncompressed_decomposition_size = MAX(uncompressed_count,manager->stats.max_uncompressed_decomposition_size); + + //order elements by sub id: order will be reversed when elements are pushed on elements stack + sort_uncompressed_elements(uncompressed_count,(SddElement*)uncompressed_elements); + //all equal subs appear consecutively now, with false subs before true subs + + SddNode* first_sub = uncompressed_elements[0].sub; + SddNode* last_sub = uncompressed_elements[uncompressed_count-1].sub; + //uncompressed_elements is not valid after calling apply (stacks may be resized and moved) + + SddSize processed_uncompressed_count = 0; + SddNodeSize compressed_count = 0; + SddNode* trimmed = NULL; + + //FIRST trimming rule: node has form T.sub, return sub + //this avoids disjoining the primes as the result must be true + if(first_sub==last_sub) { //only one distinct sub, prime must be true + trimmed = first_sub; + goto success; + } + assert(uncompressed_count >= 2); //since we have at least two distinct subs + + #ifndef NDEBUG + Vtree** vtree_loc = sdd_vtree_location(vtree,manager); //does not change + Vtree* right = vtree->right; //does not change + #endif + + //SECOND trimming rule: node has form prime.T + ~prime.F, return prime + //the following test assumes that (id of false) < (id of true); since elements + //with true subs are popped first from the stack + if(IS_FALSE(first_sub) && IS_TRUE(last_sub)) { //only two distinct subs: true and false + //disjoin only the primes of the true sub + SddNode* prime = manager->false_sdd; //will contain the disjunction of primes for the true sub + SddNode* p; SddNode* s; + uPOP_ELM(p,s,manager); ++processed_uncompressed_count; + while(IS_TRUE(s)) { //compress + assert(NON_TRIVIAL(p)); + assert(sdd_vtree_is_sub(p->vtree,vtree->left)); + //vtree->left may be changed by auto search (use vtree->left instead of saving into left) + WITH_local_auto_mode(manager, + prime = apply(p,prime,DISJOIN,manager,limited)); //only vtree->left may change + if(prime==NULL) goto failure; //closing partition failed due to time limits + assert(NON_TRIVIAL(prime)); + assert(sdd_vtree_is_sub(prime->vtree,vtree->left)); + assert(vtree==*vtree_loc); //apply does not change vtree + assert(right==vtree->right); //apply does not change right subtree + uPOP_ELM(p,s,manager); ++processed_uncompressed_count; + } + trimmed = prime; + goto success; + } + + //no trimming + + //pop uncompressed elements, compressing and placing compressed elements on element_stack + SddNode* c_prime; SddNode* c_sub; + uPOP_ELM(c_prime,c_sub,manager); ++processed_uncompressed_count; + assert(OK_PRIME(c_prime,vtree)); + + while(processed_uncompressed_count < uncompressed_count) { + SddNode* prime; SddNode* sub; + uPOP_ELM(prime,sub,manager); ++processed_uncompressed_count; + assert(OK_PRIME(prime,vtree)); + assert(OK_SUB(sub,vtree)); + if(sub==c_sub) { //compress (apply will not gc sub) + //vtree->left may be changed by auto search (use vtree->left instead of saving into left) + WITH_local_auto_mode(manager, + c_prime = apply(prime,c_prime,DISJOIN,manager,limited)); //only vtree->left may change + if(c_prime==NULL) goto failure; //closing partition failed due to time limit + assert(OK_PRIME(c_prime,vtree)); + assert(vtree==*vtree_loc); //apply does not change vtree + assert(right==vtree->right); //apply does not change right subtree + } + else { //just popped a new element + //place previous compressed element on element_stack + assert(OK_PRIME(c_prime,vtree)); + assert(OK_SUB(c_sub,vtree)); + cPUSH_ELM(c_prime,c_sub,manager); + ++compressed_count; + c_prime = prime; + c_sub = sub; + } + } + + cPUSH_ELM(c_prime,c_sub,manager); + ++compressed_count; + + //keeping track of largest decomposition + manager->stats.max_decomposition_size = MAX(compressed_count,manager->stats.max_decomposition_size); + + success: + reset_stacks(compressed_count,uncompressed_count,processed_uncompressed_count,manager); + *size = compressed_count; + *elements = manager->top_element_stack; + *node = trimmed; + return 1; + + failure: + reset_stacks(compressed_count,uncompressed_count,processed_uncompressed_count,manager); + return 0; +} + +/**************************************************************************************** + * constructing sdd nodes or looking them from a unique table + ****************************************************************************************/ + +static +SddNode* lookup_or_construct_sdd_node(SddNodeSize size, SddElement* elements, Vtree* vtree, SddManager* manager) { + + //lookup from unique table + SddHash* hash = manager->unique_nodes; + SddNode* node = lookup_sdd_node(elements,size,hash,manager); + + if(node==NULL) node = construct_decomposition_sdd_node(size,elements,vtree,manager); + + return node; +} + +/**************************************************************************************** + * ending a partition: + * + * --GET_node_of_partition: returns a node corresponding to the partition after it has + * been compressed and trimmed + * --GET_elements_of_partition: returns the partition itself after it has been trimmed + * --GET_node_of_compressed_partition: returns a node corresponding to the declared + * compressed partition (no compression or trimming) + * + ****************************************************************************************/ + +//returns 0 if closure is not successful +//otherwise, returns 1 and compressed partition (in size and elements) +//NOTE: elements array is newly allocated and can be claimed by the calling function +int GET_elements_of_partition(SddNodeSize* size, SddElement** elements, Vtree* vtree, SddManager* manager, int limited) { + + SddElement* buffer; + SddNode* trim; + + int success = compress_and_trim(size,&buffer,&trim,vtree,manager,limited); + assert(success==0 || trim==NULL); + + if(success) { + *elements = new_elements(*size,manager); + memcpy(*elements,buffer,*size*sizeof(SddElement)); + + assert(elements_sorted_and_compressed(*size,*elements)); + assert(vtree==lca_of_compressed_elements(*size,*elements,manager)); + } + + return success; +} + +//CALLED ONLY by macros GET_node_from_partition() and GET_node_from_partition_limited() +// +//returns NULL if closure is not successful +//otherwise, returns a node corresponding to the compressed partition (existing or newly created) +SddNode* GET_node_of_partition(Vtree* vtree, SddManager* manager, int limited) { + + SddNodeSize size; + SddElement* buffer; + SddNode* trim; + + int success = compress_and_trim(&size,&buffer,&trim,vtree,manager,limited); + + if(success==0) return NULL; + else if(trim) return trim; //trimming + else return lookup_or_construct_sdd_node(size,buffer,vtree,manager); +} + + +//CALLED ONLY by macro GET_node_from_compressed_partition() +// +//always succeeds +SddNode* GET_node_of_compressed_partition(Vtree* vtree, SddManager* manager) { + + //count of elements on the compression stack before this partition was opened + SddSize prev_count = POP_STACK(meta_compression_stack,manager); + + //count and array of compressed elements + SddElement* elements = manager->start_compression_stack + prev_count; + SddSize count = manager->top_compression_stack - elements; + + //reset stack top (effectively pop elements) + manager->top_compression_stack = elements; + + sort_compressed_elements(count,elements); + + assert(elements_sorted_and_compressed(count,elements)); + assert(vtree==lca_of_compressed_elements(count,elements,manager)); + + return lookup_or_construct_sdd_node(count,elements,vtree,manager); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/references.c b/pysdd/lib/libsdd-2.0/src/basic/references.c new file mode 100644 index 0000000..efd3755 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/references.c @@ -0,0 +1,79 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * updating counts and sizes + ****************************************************************************************/ + +static inline +void update_counts_and_sizes_after_livelihood_change(SddNode* node, SddManager* manager) { + assert(node->ref_count==0 || node->ref_count==1); + assert(node->type==DECOMPOSITION); + if(node->in_unique_table==0) return; + + Vtree* vtree = node->vtree; + SddSize size = node->size; + int inc = node->ref_count==1? -1: 1; + //live->dead: inc= +1 + //dead->live: inc= -1 + + //only dead counts and sizes need to be updated + manager->dead_node_count += inc; + manager->dead_sdd_size += inc*size; + vtree->dead_node_count += inc; + vtree->dead_sdd_size += inc*size; +} + +/**************************************************************************************** + * high level + ****************************************************************************************/ + +//ref_count of terminal sdds is 0 +SddRefCount sdd_ref_count(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"reference_count"); + if(IS_DECOMPOSITION(node)) return node->ref_count; + else { + assert(node->ref_count==0); + return 0; + } +} + +//returns node +SddNode* sdd_ref(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_ref"); + + if(IS_DECOMPOSITION(node) && ++node->ref_count==1) { //node was dead and became live + update_counts_and_sizes_after_livelihood_change(node,manager); + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_ref(prime,manager); + sdd_ref(sub,manager); + }); + } + + return node; +} + +//returns node +SddNode* sdd_deref(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_deref"); + CHECK_ERROR(IS_DECOMPOSITION(node) && node->ref_count==0,ERR_MSG_DEREF,"sdd_deref"); + + if(IS_DECOMPOSITION(node) && --node->ref_count==0) { //node was live and became dead + update_counts_and_sizes_after_livelihood_change(node,manager); + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_deref(prime,manager); + sdd_deref(sub,manager); + }); + } + + return node; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/replace.c b/pysdd/lib/libsdd-2.0/src/basic/replace.c new file mode 100644 index 0000000..254952e --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/replace.c @@ -0,0 +1,91 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/memory.c +void free_elements(SddNodeSize size, SddElement* elements, SddManager* manager); + +//basic/nodes.c +void declare_acquired_parent(SddNode* node, SddManager* manager); +void declare_lost_parent(SddNode* node, SddManager* manager); + +/**************************************************************************************** + * replaces the elements and vtree of a (live) decomposition node + * + * does not deal with the insertion/deletion of the node into a unique table + ****************************************************************************************/ + +//assumes node is live +//if replacement is reversible, then save current elements (instead of freeing them) +//if replacement is irreversible, then free current elements +void replace_node(int reversible, SddNode* node, SddNodeSize new_size, SddElement* new_elements, Vtree* new_vtree, SddManager* manager) { + assert(node->ref_count); //live + assert(node->type==DECOMPOSITION); //not terminal + + SddSize cur_size = node->size; + SddElement* cur_elements = ELEMENTS_OF(node); + + SddRefCount ref_count = node->ref_count; //save ref_count + while(node->ref_count) sdd_deref(node,manager); //dereference node + //node is now dead + + declare_lost_parent(node,manager); //cur_elements lost node as a parent + + //replace vtree, size and elements + node->vtree = new_vtree; + node->size = new_size; + ELEMENTS_OF(node) = new_elements; + sort_compressed_elements(new_size,new_elements); //elements must be sorted + + declare_acquired_parent(node,manager); //new_elements acquired node as a parent + + while(ref_count--) sdd_ref(node,manager); //re-establish references + //node is now live again + + //save or free current elements + if(reversible) { + node->replaced = 1; //replacement reversible + node->saved_size = cur_size; + node->saved_elements = cur_elements; + } + else { + node->replaced = 0; //replacement is irreversible + node->saved_size = 0; + node->saved_elements = NULL; + free_elements(cur_size,cur_elements,manager); + } +} + +//confirms a reversible replacement: free saved elements and insert in unique table +//once replacement is confirmed, it cannot be reversed +void confirm_node_replacement(SddNode* node, SddManager* manager) { + assert(node->replaced); //was replaced + assert(node->saved_elements); //has saved elements + assert(node->ref_count); //live + assert(node->type==DECOMPOSITION); //not terminal + + node->replaced = 0; //no longer reversible + free_elements(node->saved_size,node->saved_elements,manager); + + node->saved_size = 0; + node->saved_elements = NULL; +} + +//reverse a replacement: recover saved elements and insert in unique table +void reverse_node_replacement(SddNode* node, Vtree* vtree, SddManager* manager) { + assert(node->replaced); //was replaced + assert(node->saved_elements); //has saved elements + assert(node->ref_count); //live + assert(node->type==DECOMPOSITION); //not terminal + + int reversible = 0; //replacement is not reversible (current elements will be freed) + replace_node(reversible,node,node->saved_size,node->saved_elements,vtree,manager); +} + +/***************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/shadows.c b/pysdd/lib/libsdd-2.0/src/basic/shadows.c new file mode 100644 index 0000000..d38ed73 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/shadows.c @@ -0,0 +1,384 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/memory.c +SddElement* new_elements(SddNodeSize size, SddManager* manager); + +//basic/nodes.c +void remove_from_unique_table(SddNode* node, SddManager* manager); +void insert_in_unique_table(SddNode* node, SddManager* manager); + +//basic/replace.c +void replace_node(int reversible, SddNode* node, SddNodeSize new_size, SddElement* new_elements, Vtree* vtree, SddManager* manager); + +/**************************************************************************************** + * A shadow-DAG is constructed for a multi-rooted sdd-DAG. + * + * A shadow-DAG is used to create a light replica of an sdd-DAG, which can be used + * for various purposes, including: + * + * --recovering the sdd-DAG to its original state after it has been modified + * (for example, during a rollback of vtree operations). + * --navigate an sdd-DAG when navigation operations may change the DAG structure + * (for example, by changing the underlying vtree). + * + * To create a shadow-DAG, one needs: + * --a sdd-DAG which is defined by its root nodes (root_nodes) + * --a set of non-root nodes in the sdd DAG (changeable_nodes) + * + * The semantics and assumptions underlying shadow-DAGs are as follows: + * --all nodes in the sdd-DAG must be live when a shadow-DAG is constructed + * --after constructing a shadow-DAG, and before using it to recover the sdd-DAG: + * --sdd nodes outside the sdd-DAG remain intact + * --root_nodes and changeable_nodes are not gc'd + * --root_nodes and changeable_nodes are the only nodes whose elements may be changed + * --root_nodes and changeable_nodes are decomposition nodes + * + * When recovering the sdd-DAG from its shadow-DAG, the structures of root_nodes and + * changeable_nodes are reused. + * + * Only a subset of the sdd-DAG needs to be replicated, which is defined as follows. + * Let n be a node in the sdd-DAG which has external references from outside the + * sdd-DAG, and which does not belong to root_nodes or changeable_nodes. This will be + * called a VIRTUAL leaf of the sdd-DAG. The subset of the sdd-DAG which will be + * replaced has root_nodes as its roots, and virtual leaves as its leaves. + * + * An implication of the assumptions underlying shadow-DAGs is that virtual leaves + * will neither be gc'd nor their elements changed before an sdd-DAG is recovered + * from its shadow-DAG. + ****************************************************************************************/ + +/**************************************************************************************** + * A shadow-DAG is defined recursively as follow. + * + * A SPEC for decomposition node n consists of: + * --the size of node n. + * --shadows for its primes and subs. + * + * A SHADOW for sdd node n is one of three types: + * + * --Type 't': A pointer to node n. + * To recover node n from this shadow, we simply return the node pointer. + * + * These shadows are used only for virtual leaf nodes of the sdd-DAG. + * + * --Type 'c': A spec for decomposition node n + a pointer to node n. + * To recover node n from this shadow, we first recover its primes and subs from the + * shadow spec, and then use them to replace the current elements of node n. + * + * These shadows are used only for root_nodes and changeable_nodes. + * + * --Type 'g': A spec for decomposition node n. + * To recover node n from this shadow, we first recover its primes and subs from the + * shadow spec, and then use them to lookup the node from the unique node tables or + * recreate it. + * + * These shadows are used for all other replicated nodes of the sdd-DAG. + * + * + * Hence, leaves of the shadow-DAG are type 't' shadows, roots are type 'c' shadows, + * and internal nodes are type 'g' or type 'c' shadows. + * + * Shadow-DAGs are supported by three functions: + * + * --shadows_new() + * --shadows_recover() + * --shadows_free() + ****************************************************************************************/ + +#define NODE_OF(S) (S->alpha.node) +#define ELMS_OF(S) (S->alpha.elements) + +#define INTERNAL_g(S) (S->size>0 && S->reuse==0) +#define INTERNAL_c(S) (S->size>0 && S->reuse==1) + +SddNode* shadow_node(NodeShadow* shadow) { + assert(shadow->size==0); + return NODE_OF(shadow); +} + +ElmShadow* shadow_elements(NodeShadow* shadow) { + assert(shadow->size>0); + return ELMS_OF(shadow); +} + +int shadow_is_terminal(NodeShadow* shadow) { + return shadow->size==0; +} +int shadow_is_internal(NodeShadow* shadow) { + return shadow->size>0; +} + +/**************************************************************************************** + * allocating and freeing shadows and specs + ****************************************************************************************/ + +static +NodeShadow* leaf_shadow_new(SddNode* node, SddShadows* shadows) { + assert(node); + assert(node->shadow_type=='t'); + ++shadows->shadow_count; + shadows->shadow_byte_count += sizeof(NodeShadow); + + NodeShadow* shadow; + MALLOC(shadow,NodeShadow,"leaf_shadow_new"); + + sdd_ref(node,shadows->manager); //protect + + NODE_OF(shadow) = node; + shadow->vtree = node->vtree; + shadow->size = 0; + shadow->ref_count = 1; + shadow->cache = NULL; + shadow->bit = 0; + shadow->reuse = 0; + return shadow; +} + +static +NodeShadow* internal_shadow_new(SddNode* node, SddShadows* shadows) { + assert(node->shadow_type=='g' || node->shadow_type=='c'); + ++shadows->shadow_count; + shadows->shadow_byte_count += sizeof(NodeShadow) + node->size*sizeof(ElmShadow); + + NodeShadow* shadow; + MALLOC(shadow,NodeShadow,"internal_shadow_new"); + + CALLOC(ELMS_OF(shadow),ElmShadow,node->size,"internal_shadow_new"); + shadow->vtree = node->vtree; + shadow->size = node->size; + shadow->ref_count = 1; + shadow->cache = node->shadow_type=='c'? node: NULL; //reuse node structure + shadow->bit = 0; + shadow->reuse = node->shadow_type=='c'; + return shadow; +} + +static +void leaf_shadow_free(NodeShadow* shadow, SddShadows* shadows) { + assert(shadow_is_terminal(shadow)); + assert(shadows->shadow_count); + --shadows->shadow_count; + shadows->shadow_byte_count -= sizeof(NodeShadow); + + SddNode* node = NODE_OF(shadow); + if(node) sdd_deref(node,shadows->manager); //release + + free(shadow); +} + +/**************************************************************************************** + * converting shadow types + ****************************************************************************************/ + +static +void convert_internal_shadow_to_leaf(SddNode* node, NodeShadow* shadow, SddShadows* shadows) { + assert(shadow_is_internal(shadow)); + shadows->shadow_byte_count -= shadow->size*sizeof(ElmShadow); + + if(node) sdd_ref(node,shadows->manager); //protect + + free(ELMS_OF(shadow)); //before setting node + NODE_OF(shadow) = node; //may be NULL (when freeing shadows) + shadow->vtree = node? node->vtree: NULL; + shadow->size = 0; + //ref_count, cache, bit, reuse unchanged +} + +/**************************************************************************************** + * constructing shadows for sdd nodes + * + * assumes node->shadow has been initialized to NULL, and node->shadow_type has been set + ****************************************************************************************/ + +static +NodeShadow* shadow_from_node(SddNode* node, SddShadows* shadows) { + assert(node->shadow_type=='t' || node->shadow_type=='g' || node->shadow_type=='c'); + + if(node->shadow) { //already constructed + ++node->shadow->ref_count; //additional reference + return node->shadow; + } + + //construct shadow + NodeShadow* shadow; + + if(node->shadow_type=='t') shadow = leaf_shadow_new(node,shadows); + else { + assert(node->type==DECOMPOSITION); + shadow = internal_shadow_new(node,shadows); + SddElement* elements = ELEMENTS_OF(node); //shadow these elements + ElmShadow* s_elements = ELMS_OF(shadow); + for(SddNodeSize i=0; isize; i++) { + s_elements[i].prime = shadow_from_node(elements[i].prime,shadows); + s_elements[i].sub = shadow_from_node(elements[i].sub,shadows); + } + } + + return node->shadow = shadow; +} + +/**************************************************************************************** + * recovering a node from its shadow (frees shadows along the way) + ****************************************************************************************/ + +static +SddNode* node_from_shadow(NodeShadow* shadow, SddShadows* shadows) { + assert(shadow->ref_count); + + SddNode* node; + + if(shadow_is_terminal(shadow)) node = NODE_OF(shadow); //terminal shadow: lookup node + else { + SddManager* manager = shadows->manager; + SddNodeSize size = shadow->size; + Vtree* vtree = shadow->vtree; + ElmShadow* s_elements = ELMS_OF(shadow); + + if(INTERNAL_g(shadow)) { //internal shadow with no reuse: lookup or recreate node + GET_node_from_compressed_partition(node,vtree,manager,{ + for(SddNodeSize i=0; ivtree==vtree); + } + else { //internal shadow with reuse: replace elements/vtree of saved node structure + node = shadow->cache; + assert(node->in_unique_table); + SddElement* elements = new_elements(size,manager); + for(SddNodeSize i=0; iref_count==0) leaf_shadow_free(shadow,shadows); //shadow no longer needed + + return node; + } + +/**************************************************************************************** + * traversing a shadow and its descendants, applying function to each visited shadow + ****************************************************************************************/ + +void shadow_traverse(int bit, NodeShadow* shadow, void (*fn)(NodeShadow*,SddShadows*), SddShadows* shadows) { + if(shadow->bit==bit) return; + shadow->bit = bit; + (*fn)(shadow,shadows); //apply function to shadow + if(shadow_is_internal(shadow)) { + ElmShadow* elements = ELMS_OF(shadow); + for(ElmShadow* e=elements; esize; e++) { + shadow_traverse(bit,e->prime,fn,shadows); + shadow_traverse(bit,e->sub,fn,shadows); + } + } +} + +/**************************************************************************************** + * freeing a node shadow + ****************************************************************************************/ + +static +void shadow_free(NodeShadow* shadow, SddShadows* shadows) { + assert(shadow); + assert(shadow->ref_count); + + if(shadow_is_internal(shadow)) { + ElmShadow* elements = ELMS_OF(shadow); + for(SddNodeSize i=0; isize; i++) { + shadow_free(elements[i].prime,shadows); + shadow_free(elements[i].sub,shadows); + } + //elements no longer needed: convert to leaf shadow + convert_internal_shadow_to_leaf(NULL,shadow,shadows); //will free elements + } + + if(--shadow->ref_count==0) leaf_shadow_free(shadow,shadows); //shadow no longer needed +} + +/**************************************************************************************** + * Shadows interface: constructing, recovering and freeing shadows + ****************************************************************************************/ + +//constructing +//assumes node->shadow and node->shadow_type has been initialized for all relevant nodes +SddShadows* shadows_new(SddSize root_count, SddNode** root_nodes, SddManager* manager) { + + SddShadows* shadows; + MALLOC(shadows,SddShadows,"shadows_new"); + + shadows->manager = manager; + shadows->root_count = root_count; + shadows->root_shadows = NULL; + shadows->shadow_count = 0; + shadows->shadow_byte_count = 0; + shadows->bit = 0; + + if(root_count==0) return shadows; + + //construct and save root shadows + CALLOC(shadows->root_shadows,NodeShadow*,root_count,"shadows_new"); + for(SddSize i=0; iroot_shadows[i] = shadow_from_node(node,shadows); + } + + assert(shadows->shadow_count); + return shadows; +} + +//recovering +//will free shadows as a side effect +void shadows_recover(SddShadows* shadows) { + assert(shadows->shadow_count); //shadows have been saved + + for(SddSize i=0; iroot_count; i++) node_from_shadow(shadows->root_shadows[i],shadows); + + //all shadows now freed + assert(shadows->shadow_count==0); + assert(shadows->shadow_byte_count==0); + + free(shadows->root_shadows); + free(shadows); +} + +//traversing +void shadows_traverse(void (*fn)(NodeShadow*,SddShadows*), SddShadows* shadows) { + int bit = shadows->bit = !shadows->bit; + for(SddSize i=0; iroot_count; i++) { + shadow_traverse(bit,shadows->root_shadows[i],fn,shadows); + } +} + +//freeing +void shadows_free(SddShadows* shadows) { + assert(shadows->shadow_count!=0 || shadows->root_count==0); + + for(SddSize i=0; iroot_count; i++) shadow_free(shadows->root_shadows[i],shadows); + + assert(shadows->shadow_count==0); + assert(shadows->shadow_byte_count==0); + + free(shadows->root_shadows); + free(shadows); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/basic/sort.c b/pysdd/lib/libsdd-2.0/src/basic/sort.c new file mode 100644 index 0000000..5fc5585 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/basic/sort.c @@ -0,0 +1,148 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * sorting nodes and elements + ****************************************************************************************/ + +/**************************************************************************************** + * sorting a linked list of nodes (linked using the ->next field) + * + * this is use to process nodes according to their size (e.g., in rotate and swap) + ****************************************************************************************/ + +//compare nodes based on size then id (no two nodes are equal according to this function, +//which leaves no ambiguity when sorting) +static inline +int size_cmp(const void* n1, const void* n2) { + const SddNodeSize s1 = (*((const SddNode**)n1))->size; + const SddNodeSize s2 = (*((const SddNode**)n2))->size; + if(s1 > s2) return 1; //(s1 > s2): smaller to larger + else if(s1 < s2) return -1; //(s1 < s2): smaller to larger + else { + const SddSize id1 = (*((const SddNode**)n1))->id; + const SddSize id2 = (*((const SddNode**)n2))->id; + if(id1 > id2) return 1; + else if(id1 < id2) return -1; + else return 0; + } +} + +//sort a linked list of sdd nodes +void sort_linked_nodes(SddSize count, SddNode** list, SddManager* manager) { + if(count<2) return; + + //make sure node buffer is big enough + if(count > manager->node_buffer_size) { + manager->node_buffer_size = 2*count; + REALLOC(manager->node_buffer,SddNode*,manager->node_buffer_size,"sort_linked_nodes"); + } + + SddNode** buffer = manager->node_buffer; + + //place nodes in buffer, then sort + FOR_each_linked_node(n,*list,*buffer++=n); buffer -= count; + qsort((SddNode**)buffer,count,sizeof(SddNode*),size_cmp); + //put sorted nodes back into a linked list + while(--count) { (*buffer)->next = *(buffer+1); ++buffer; } + (*buffer)->next = NULL; //last node + + *list = manager->node_buffer[0]; //first node + //check smaller to larger order + assert((*list)->size <= (*list)->next->size); //smaller to larger size +} + +/**************************************************************************************** + * sorting an array of elements + * + * elements of a node are sorted for + * --efficient lookup from unique node tables + * --use during element compression (expects the elements to be sorted) + * + * elements are sorted by the id of subs (this leaves no ambiguity when sorting + * compressed elements) + ****************************************************************************************/ + +//sort elements from larger to smaller sub->id +// then from smaller to larger prime sizes +// then from smaller to larger prime ids +// +//Note: want no ambiguity in the final order of elements, otherwise the sort is unstable, +//producing different results on different machines +// +static inline +int cmp_by_sub_id_L(const void* e1, const void* e2) { + //sort by id of sub: smaller to larger id + const SddSize sid1 = ((const SddElement*)e1)->sub->id; + const SddSize sid2 = ((const SddElement*)e2)->sub->id; + if(sid1 > sid2) return 1; + else if(sid1 < sid2) return -1; + else { + //sort by prime size: smaller to larger + //this affects order of disjoining primes during compression + //this appears more efficient than sorting larger to smaller + const SddNodeSize ps1 = ((const SddElement*)e1)->prime->size; + const SddNodeSize ps2 = ((const SddElement*)e2)->prime->size; + if(ps1 > ps2) return 1; + else if(ps1 < ps2) return -1; + else { + //sort by prime id: smaller to larger + //so the element order is unique + //without this, final element order may depend on system + const SddSize pid1 = ((const SddElement*)e1)->prime->id; + const SddSize pid2 = ((const SddElement*)e2)->prime->id; + if(pid1 > pid2) return 1; + else if(pid1 < pid2) return -1; + else return 0; + } + } +} + +//sort by id of sub: larger to smaller sub->id +//used to sort elements that are compressed (i.e., with distinct) +static inline +int cmp_by_sub_id_G(const void* e1, const void* e2) { + const SddSize sid1 = ((const SddElement*)e1)->sub->id; + const SddSize sid2 = ((const SddElement*)e2)->sub->id; + if(sid1 < sid2) return 1; + else if(sid1 > sid2) return -1; + else { + assert(0); //should not reach this since subs are distinct + return 0; + } +} + +//sort elements from larger to smaller sub->id (then other tie breakers -- see above) +//equal subs will be adjacent in the sorted list +// +//NOTE: this is using cmp_by_sub_id_L (above) instead of cmp_by_sub_id_G (below) since +//the order of these elements will be reversed later when pushed on the elements stack +// +void sort_uncompressed_elements(SddSize size, SddElement* elements) { + qsort((SddElement*)elements,size,sizeof(SddElement),cmp_by_sub_id_L); +} + +//check that elements are sorted (from larger to smaller sub->id) +//check that all subs are distinct (i.e., compressed complements) +int elements_sorted_and_compressed(SddNodeSize size, SddElement* elements) { + assert(size > 1); + for(SddNodeSize i=1; iid <= elements[i].sub->id) return 0; + } + return 1; +} + +//sort elements from larger to smaller sub->id +//assumes that subs are distinct (i.e., their ids are distinct) +void sort_compressed_elements(SddNodeSize size, SddElement* elements) { + qsort((SddElement*)elements,size,sizeof(SddElement),cmp_by_sub_id_G); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/manager/copy.c b/pysdd/lib/libsdd-2.0/src/manager/copy.c new file mode 100644 index 0000000..39b19cd --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/manager/copy.c @@ -0,0 +1,84 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * copying manager + * + * no external references to nodes in copied manager (all nodes are dead) + * new manager has defaults (e.g., auto-mode, etc) + ****************************************************************************************/ + +static +void initialize_decomposition_and_literal_maps(Vtree* from_vtree, SddManager* from_manager, SddManager* into_manager) { + if(LEAF(from_vtree)) { + SddLiteral var = from_vtree->var; + sdd_manager_literal(+var,from_manager)->map = sdd_manager_literal(+var,into_manager); + sdd_manager_literal(-var,from_manager)->map = sdd_manager_literal(-var,into_manager); + } + else { + FOR_each_sdd_node_normalized_for(node,from_vtree,node->map = NULL); + initialize_decomposition_and_literal_maps(from_vtree->left,from_manager,into_manager); + initialize_decomposition_and_literal_maps(from_vtree->right,from_manager,into_manager); + } +} + +static +void copy_decomposition_nodes(Vtree* from_vtree, Vtree* into_vtree, SddManager* into_manager) { + if(LEAF(from_vtree)) { + assert(LEAF(into_vtree)); + } + else { + //bottom up copying + copy_decomposition_nodes(from_vtree->left,into_vtree->left,into_manager); + copy_decomposition_nodes(from_vtree->right,into_vtree->right,into_manager); + + FOR_each_sdd_node_normalized_for(node,from_vtree,{ + GET_node_from_compressed_partition(node->map,into_vtree,into_manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + DECLARE_compressed_element(prime->map,sub->map,into_vtree,into_manager); + }); + }); + }); + } +} + +//return a copy of manager +//nodes are in manager and there are size of them +//replace these nodes by their copies in the new manager +SddManager* sdd_manager_copy(SddSize size, SddNode** nodes, SddManager* from_manager) { + + Vtree* from_vtree = from_manager->vtree; + SddManager* into_manager = sdd_manager_new(from_vtree); + Vtree* into_vtree = into_manager->vtree; + //literal and constant sdds already copied + + //initialize node maps + sdd_manager_true(from_manager)->map = sdd_manager_true(into_manager); + sdd_manager_false(from_manager)->map = sdd_manager_false(into_manager); + initialize_decomposition_and_literal_maps(from_vtree,from_manager,into_manager); + + //copy decomposition nodes of manager into new_manager + copy_decomposition_nodes(from_vtree,into_vtree,into_manager); + + assert(from_manager->node_count==into_manager->node_count); + assert(from_manager->sdd_size==into_manager->sdd_size); + assert(into_manager->node_count==into_manager->dead_node_count); + assert(into_manager->sdd_size==into_manager->dead_sdd_size); + + //replace manager nodes by their copies in new manager + for(SddSize i=0; imap); + nodes[i] = nodes[i]->map; + } + + return into_manager; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/manager/interface.c b/pysdd/lib/libsdd-2.0/src/manager/interface.c new file mode 100644 index 0000000..b8a3a35 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/manager/interface.c @@ -0,0 +1,411 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" +#include + +//vtrees/vtree.c +void set_vtree_properties(Vtree* vtree); + +/**************************************************************************************** + * (Ctrl+\) can be used to print the last constructed sdd manager + * + * this is used to see what's going on in case the manager is stuck while computing + ****************************************************************************************/ + +//a signal handling function for user interuption (Ctrl+\) +void SIGQUIT_handler(int signum) { + extern SddManager* last_constructed_manager; + if(last_constructed_manager!=NULL) { + printf("\n---"); + sdd_manager_print(last_constructed_manager); + char c = last_constructed_manager->vtree_ops.current_op; + if(c!=' ') { + printf("\ncurrent vtree operation: %c\n",c); + } + printf("---\n"); + } +} + +void declare_interrupt_signal() { +#ifdef SIGQUIT + signal(SIGQUIT,SIGQUIT_handler); +#endif +} + + +/**************************************************************************************** + * dynamic garbage collection and vtree search + ****************************************************************************************/ + +void sdd_manager_auto_gc_and_minimize_on(SddManager* manager) { + manager->auto_gc_and_search_on = 1; +} + +void sdd_manager_auto_gc_and_minimize_off(SddManager* manager) { + manager->auto_gc_and_search_on = 0; +} + +int sdd_manager_is_auto_gc_and_minimize_on(SddManager* manager) { + return manager->auto_gc_and_search_on; +} + +void sdd_manager_set_minimize_function(SddVtreeSearchFunc f, SddManager* manager) { + manager->vtree_search_function = f; +} + +void sdd_manager_unset_minimize_function(SddManager* manager) { + manager->vtree_search_function = NULL; +} + +/**************************************************************************************** + * basic manager lookups + ****************************************************************************************/ + +//returns number of variables in a manager +SddLiteral sdd_manager_var_count(SddManager* manager) { + return manager->var_count; +} + +/**************************************************************************************** + * SDD navigation + ****************************************************************************************/ + +int sdd_node_is_true(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_is_true"); + return node->type==TRUE; +} + +int sdd_node_is_false(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_is_false"); + return node->type==FALSE; +} + +int sdd_node_is_literal(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_is_literal"); + return node->type==LITERAL; +} + +int sdd_node_is_decision(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_is_decision"); + return node->type==DECOMPOSITION; +} + +SddNodeSize sdd_node_size(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_node_size"); + return node->size; +} + +SddLiteral sdd_node_literal(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_literal_of"); + CHECK_ERROR(node->type!=LITERAL,ERR_MSG_NODE_ITR,"sdd_literal_of"); + return LITERAL_OF(node); +} + +SddNode** sdd_node_elements(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_node_elements"); + CHECK_ERROR(node->type!=DECOMPOSITION,ERR_MSG_NODE_ITR,"sdd_node_elements"); + return (SddNode**) ELEMENTS_OF(node); +} + +void sdd_node_set_bit(int bit, SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_node_set_bit"); + node->user_bit = bit; +} + +int sdd_node_bit(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_node_bit"); + return node->user_bit; +} + + +SddSize test_n(SddNode* node) { + if(sdd_node_bit(node)) return 0; + else { + sdd_node_set_bit(1,node); + SddSize size = 0; + if(sdd_node_is_decision(node)) { + SddNodeSize s = sdd_node_size(node); + SddNode** e = sdd_node_elements(node); + size += s; + while(s--) { + size += test_n(*e++); + size += test_n(*e++); + } + } + return size; + } +} + +/**************************************************************************************** + * size and count + ****************************************************************************************/ + +//total size of all sdd nodes +SddSize sdd_manager_size(const SddManager* manager) { + return manager->sdd_size; +} + +//total size of live sdd nodes +SddSize sdd_manager_live_size(const SddManager* manager) { + return manager->sdd_size-manager->dead_sdd_size; +} + +//total size of dead sdd nodes +SddSize sdd_manager_dead_size(const SddManager* manager) { + return manager->dead_sdd_size; +} + +//total count of all sdd nodes +SddSize sdd_manager_count(const SddManager* manager) { + return manager->node_count; +} + +//total count of live sdd nodes +SddSize sdd_manager_live_count(const SddManager* manager) { + return manager->node_count-manager->dead_node_count; +} + +//total count of dead sdd nodes +SddSize sdd_manager_dead_count(const SddManager* manager) { + return manager->dead_node_count; +} + +/**************************************************************************************** + * terminal SDD nodes + ****************************************************************************************/ + +//returns a true sdd that is normalized for the root of the manager's vtree +SddNode* sdd_manager_true(const SddManager* manager) { + return manager->true_sdd; +} + +//returns a false sdd that is normalized for the root of the manager's vtree +SddNode* sdd_manager_false(const SddManager* manager) { + return manager->false_sdd; +} + +//returns the sdd corresponding to a literal, normalized for corresponding leaf vtree +//literal is an integer <> 0 +SddNode* sdd_manager_literal(const SddLiteral literal, const SddManager* manager) { + return manager->literals[literal]; +} + +/**************************************************************************************** + * sdd functions + ****************************************************************************************/ + +SddSize sdd_id(SddNode* node) { + return node->id; +} + +//returns 1 if the node having id has been garbage collected +//the test GC_NODE(node) may not succeed even though the node may have been gc'd +//this happens when the node structure has been reused by another newly created node +//in this case, the test node->id!=id will succeed +int sdd_garbage_collected(SddNode* node, SddSize id) { + return GC_NODE(node) || node->id!=id; +} + +Vtree* sdd_vtree_of(SddNode* node) { + return node->vtree; +} + +SddNode* sdd_conjoin(SddNode* node1, SddNode* node2, SddManager* manager) { + return sdd_apply(node1,node2,CONJOIN,manager); +} + +SddNode* sdd_disjoin(SddNode* node1, SddNode* node2, SddManager* manager) { + return sdd_apply(node1,node2,DISJOIN,manager); +} + +/**************************************************************************************** + * garbage collection + ****************************************************************************************/ + +//runs a global garbage collection on sdd nodes +void sdd_manager_garbage_collect(SddManager* manager) { + sdd_vtree_garbage_collect(manager->vtree,manager); +} + +//runs a global garbage collection when the number of dead nodes exceeds a certain threshold +//this is more efficient than calling sdd_manager_garbage_collect_if on vtree of manager +int sdd_manager_garbage_collect_if(float dead_node_threshold, SddManager* manager) { + SddSize dead_node_count = sdd_manager_dead_count(manager); //more efficient + SddSize total_node_count = sdd_manager_count(manager); //more efficient + if(dead_node_count > total_node_count*dead_node_threshold) { + sdd_manager_garbage_collect(manager); + return 1; + } + else return 0; +} + +//runs a local garbage collection when the number of dead nodes exceeds a certain threshold +int sdd_vtree_garbage_collect_if(float dead_node_threshold, Vtree* vtree, SddManager* manager) { + SddSize dead_node_count = sdd_vtree_dead_count(vtree); + SddSize total_node_count = sdd_vtree_count(vtree); + if(dead_node_count > total_node_count*dead_node_threshold) { + sdd_vtree_garbage_collect(vtree,manager); + return 1; + } + else return 0; +} + +/**************************************************************************************** + * vtree search (sdd minimization) + ****************************************************************************************/ + +void sdd_manager_minimize(SddManager* manager) { + sdd_vtree_minimize(sdd_manager_vtree(manager),manager); +} + +void sdd_manager_minimize_limited(SddManager* manager) { + sdd_vtree_minimize_limited(sdd_manager_vtree(manager),manager); +} + + +// these are checked by exceeded_limits(), invoked by l_apply +void sdd_manager_set_vtree_search_time_limit(clock_t time_limit, SddManager* manager) { + manager->vtree_ops.search_time_limit = time_limit*CLOCKS_PER_SEC; +} +void sdd_manager_set_vtree_fragment_time_limit(clock_t time_limit, SddManager* manager) { + manager->vtree_ops.fragment_time_limit = time_limit*CLOCKS_PER_SEC; +} +void sdd_manager_set_vtree_operation_time_limit(clock_t time_limit, SddManager* manager) { + manager->vtree_ops.op_time_limit = time_limit*CLOCKS_PER_SEC; +} +void sdd_manager_set_vtree_apply_time_limit(clock_t time_limit, SddManager* manager) { + manager->vtree_ops.apply_time_limit = time_limit*CLOCKS_PER_SEC; +} +void sdd_manager_set_vtree_operation_memory_limit(float memory_limit, SddManager* manager) { + manager->vtree_ops.op_memory_limit = memory_limit; +} + +// this is checked by exceeded_size_limit(), invoked by vtree operations +void sdd_manager_set_vtree_operation_size_limit(float size_limit, SddManager* manager) { + manager->vtree_ops.op_size_limit = size_limit; +} + +void sdd_manager_set_vtree_search_convergence_threshold(float threshold, SddManager* manager) { + manager->vtree_ops.convergence_threshold = threshold; +} + +void sdd_manager_set_vtree_cartesian_product_limit(SddSize size_limit, SddManager* manager) { + manager->vtree_ops.cartesian_product_limit = size_limit; +} + +/**************************************************************************************** + * manager options + ****************************************************************************************/ + +void* sdd_manager_options(SddManager* manager) { + return manager->options; +} + +void sdd_manager_set_options(void* options, SddManager* manager) { + manager->options = options; +} + +/**************************************************************************************** + * manager vtree and order + ****************************************************************************************/ + +//returns a copy of the vtree for manager +Vtree* sdd_manager_vtree_copy(const SddManager* manager) { + Vtree* vtree = copy_vtree(manager->vtree); + set_vtree_properties(vtree); + return vtree; +} + +//returns vtree of manager +Vtree* sdd_manager_vtree(const SddManager* manager) { + return manager->vtree; +} + + +//fills the given array with manager variables according to their vtree inorder +//the array length must be equal to the number of manager variables +void sdd_manager_var_order(SddLiteral* var_order, SddManager *manager) { + void var_order_aux(SddLiteral** var_order_loc, Vtree* vtree); + var_order_aux(&var_order,manager->vtree); +} + +void var_order_aux(SddLiteral** var_order_loc, Vtree* vtree) { + if(LEAF(vtree)) { + **var_order_loc = vtree->var; + (*var_order_loc)++; + } + else { + var_order_aux(var_order_loc,vtree->left); + var_order_aux(var_order_loc,vtree->right); + } +} + +/**************************************************************************************** + * vtree traversal + ****************************************************************************************/ + +Vtree* sdd_vtree_left(const Vtree* vtree) { + return vtree->left; +} + +Vtree* sdd_vtree_right(const Vtree* vtree) { + return vtree->right; +} + +Vtree* sdd_vtree_parent(const Vtree* vtree) { + return vtree->parent; +} + +/**************************************************************************************** + * vtree properties + ****************************************************************************************/ + +SddLiteral sdd_vtree_var_count(const Vtree* vtree) { + return vtree->var_count; +} + +SddLiteral sdd_vtree_var(const Vtree* vtree) { + if(LEAF(vtree)) return vtree->var; + else return 0; +} + +SddLiteral sdd_vtree_position(const Vtree* vtree) { + return vtree->position; +} + +/**************************************************************************************** + * vtree state + ****************************************************************************************/ + +void sdd_vtree_set_bit(int bit, Vtree* vtree) { + vtree->user_bit = bit; +} + +int sdd_vtree_bit(const Vtree* vtree) { + return vtree->user_bit; +} + +void sdd_vtree_set_data(void* data, Vtree* vtree) { + vtree->user_data = data; +} + +void* sdd_vtree_data(const Vtree* vtree) { + return vtree->user_data; +} + +void sdd_vtree_set_search_state(void* search_state, Vtree* vtree) { + vtree->user_search_state = search_state; +} + +void* sdd_vtree_search_state(const Vtree* vtree) { + return vtree->user_search_state; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/manager/manager.c b/pysdd/lib/libsdd-2.0/src/manager/manager.c new file mode 100644 index 0000000..005b746 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/manager/manager.c @@ -0,0 +1,308 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//basic/hash.c +SddHash* new_unique_node_hash(SddManager* manager); +void free_hash(SddHash* hash); + +//basic/nodes.c +void free_sdd_node(SddNode* node, SddManager* manager); +SddNode* construct_literal_sdd_node(SddLiteral literal, Vtree* vtree, SddManager* manager); +SddNode* construct_true_sdd_node(SddManager* manager); +SddNode* construct_false_sdd_node(SddManager* manager); + +//vtrees/vtree.c +void set_vtree_properties(Vtree* vtree); + +//local declarations +static void setup_terminal_sdds(SddManager* manager); + +/**************************************************************************************** + * constructing manager + ****************************************************************************************/ + +//used by interrupt signal +SddManager* last_constructed_manager = NULL; + +//constructs a new manager and associated sdd nodes +SddManager* sdd_manager_new(Vtree* input_vtree) { + + CHECK_ERROR(input_vtree==NULL,ERR_MSG_INPUT_VTREE,"new_sdd_manager"); + + //work with a copy of the passed vtree + //input vtree will remain unchanged + Vtree* vtree = copy_vtree(input_vtree); + + //positions, var counts, and linked list + set_vtree_properties(vtree); + + //allocate manager + SddManager* manager; + MALLOC(manager,SddManager,"new_sdd_manager"); + + //initializations + SddLiteral var_count = vtree->var_count; + manager->vtree = vtree; + manager->id_counter = 0; + manager->var_count = var_count; + + //basic counts + manager->node_count = 0; + manager->dead_node_count = 0; + manager->computed_count = 0; + + //sizes + manager->sdd_size = 0; + manager->dead_sdd_size = 0; + + //gc lists + manager->gc_node_count = 0; + manager->gc_element_count = 0; + + CALLOC(manager->gc_node_lists,SddNode*,GC_BUCKETS_COUNT,"new_sdd_manager"); + + //unique nodes + manager->unique_nodes = new_unique_node_hash(manager); + + //computation caches + manager->computed_cache_lookup_count = 0; + manager->computed_cache_hit_count = 0; + CALLOC(manager->conjoin_cache,SddComputed,COMPUTED_CACHE_SIZE,"new_sdd_manager"); + CALLOC(manager->disjoin_cache,SddComputed,COMPUTED_CACHE_SIZE,"new_sdd_manager"); + + //apply + manager->apply_depth = 0; + manager->limited_apply_depth = 0; + + //literals and vtree leaves + + //indexing literal sdds: -var_count, ..., -2, -1, NULL, +1, +2, ..., +var_count + CALLOC(manager->literals,SddNode*,1+2*var_count,"new_sdd_manager"); + manager->literals += var_count; //positioned at NULL + + //indexing leaf vtrees: NULL, 1, 2, ..., var_count + CALLOC(manager->leaf_vtrees,Vtree *,1+var_count,"new_sdd_manager"); + + //stacks + CALLOC(manager->start_compression_stack,SddElement,INITIAL_SIZE_ELEMENT_STACK,"new_sdd_manager"); + manager->top_compression_stack = manager->start_compression_stack; + manager->capacity_compression_stack = INITIAL_SIZE_ELEMENT_STACK; + + CALLOC(manager->start_cp_stack1,SddElement,INITIAL_SIZE_ELEMENT_STACK,"new_sdd_manager"); + manager->top_cp_stack1 = manager->start_cp_stack1; + manager->capacity_cp_stack1 = INITIAL_SIZE_ELEMENT_STACK; + + CALLOC(manager->start_cp_stack2,SddElement,INITIAL_SIZE_ELEMENT_STACK,"new_sdd_manager"); + manager->top_cp_stack2 = manager->start_cp_stack2; + manager->capacity_cp_stack2 = INITIAL_SIZE_ELEMENT_STACK; + + CALLOC(manager->start_cp_stack3,SddElement,INITIAL_SIZE_ELEMENT_STACK,"new_sdd_manager"); + manager->top_cp_stack3 = manager->start_cp_stack3; + manager->capacity_cp_stack3 = INITIAL_SIZE_ELEMENT_STACK; + + CALLOC(manager->start_meta_compression_stack,SddSize,INITIAL_SIZE_COMPRESSION_STACK,"new_sdd_manager"); + manager->top_meta_compression_stack = manager->start_meta_compression_stack; + manager->capacity_meta_compression_stack = INITIAL_SIZE_COMPRESSION_STACK; + + CALLOC(manager->start_element_stack,SddElement,INITIAL_SIZE_ELEMENT_STACK,"new_sdd_manager"); + manager->top_element_stack = manager->start_element_stack; + manager->capacity_element_stack = INITIAL_SIZE_ELEMENT_STACK; + + CALLOC(manager->node_buffer,SddNode*,INITIAL_SIZE_NODE_BUFFER,"new_sdd_manager"); + manager->node_buffer_size = INITIAL_SIZE_NODE_BUFFER; + + //manager options + manager->options = NULL; + + //manager stats + SddManagerStats stats = {0,0,0,0,0,0,0,0}; + manager->stats = stats; + + //vtree search + SddManagerVtreeOps vtree_ops = {VTREE_SEARCH_TIME_LIMIT, + VTREE_FRAGMENT_TIME_LIMIT, + VTREE_OP_TIME_LIMIT, + VTREE_APPLY_TIME_LIMIT, + 0,0,0,0,0,0,0,0,0,0, + VTREE_OP_SIZE_LIMIT, + 0,VTREE_OP_MEMORY_LIMIT, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0, + ' ', + INITIAL_CONVERGENCE_THRESHOLD, + CARTESIAN_PRODUCT_LIMIT}; + manager->vtree_ops = vtree_ops; + + //automatic garbage collection and search + manager->auto_local_gc_and_search_on = 0; + manager->auto_gc_and_search_on = 0; + manager->auto_vtree_search_on = 0; + manager->auto_apply_vtree = NULL; + manager->auto_apply_outside_live_size = 0; + manager->auto_apply_outside_live_count = 0; + manager->auto_apply_outside_dead_count = 0; + manager->auto_search_invocation_count = 0; + manager->auto_search_invocation_count_global = 0; + manager->auto_search_invocation_count_local = 0; + manager->auto_search_invocation_count_recursive = 0; + manager->auto_search_invocation_count_aborted_apply = 0; + manager->auto_search_invocation_count_aborted_operation = 0; + manager->auto_search_invocation_count_aborted_fragment = 0; + manager->auto_search_invocation_count_aborted_search = 0; + manager->auto_search_iteration_count = 0; + manager->auto_gc_invocation_count = 0; + manager->auto_search_reduction_sum = 0; + manager->vtree_search_function = NULL; + + //vtree fragments + manager->max_fragment_shadow_count = 0; + manager->max_fragment_shadow_byte_count = 0; + manager->fragment_count = 0; + manager->completed_fragment_count = 0; + manager->backward_completed_fragment_count = 0; + manager->successful_fragment_count = 0; + manager->backward_successful_fragment_count = 0; + manager->successful_completed_fragment_count = 0; + + //terminal sdds + setup_terminal_sdds(manager); //must be done after setting properties + + last_constructed_manager = manager; + declare_interrupt_signal(); //catch Ctrl+\ interrupts + + return manager; +} + +//constructs a new manager using only a variable count +//uses a balanced vtree by default +//turns on auto mode +SddManager* sdd_manager_create(SddLiteral var_count, int auto_gc_and_minimize) { + CHECK_ERROR(var_count<1,ERR_MSG_ONE_VAR,"sdd_manager_create"); + Vtree* vtree = sdd_vtree_new(var_count,"balanced"); + SddManager* manager = sdd_manager_new(vtree); + if(auto_gc_and_minimize) sdd_manager_auto_gc_and_minimize_on(manager); //auto mode + sdd_vtree_free(vtree); //no longer needed (manager makes a copy) + return manager; +} + +/**************************************************************************************** + * initializing sdd manager + ****************************************************************************************/ + +//construct and index true and false for manager +void setup_true_false_sdds(SddManager* manager) { + //construct + //CRITICAL to construct false first and true immediately next so that + //(id of false) = (id of true)-1 + //partitions.c depends crtitically on this + SddNode* false_sdd = construct_false_sdd_node(manager); + SddNode* true_sdd = construct_true_sdd_node(manager); + assert(true_sdd->id==1+false_sdd->id); + //index + manager->true_sdd = true_sdd; + manager->false_sdd = false_sdd; + //cache negations + true_sdd->negation = false_sdd; + false_sdd->negation = true_sdd; +} + +//construct and index literal sdds +void setup_literal_sdds(Vtree* vtree, SddManager* manager) { + FOR_each_leaf_vtree_node(v,vtree,{ + //construct + SddLiteral var = v->var; + SddNode* plit = construct_literal_sdd_node(var,v,manager); + SddNode* nlit = construct_literal_sdd_node(-var,v,manager); + //declare as sdd nodes of vtree + v->nodes = plit; //first + plit->vtree_next = nlit; //second + nlit->vtree_next = NULL; + v->node_count = 2; + //index + manager->literals[var] = plit; + manager->literals[-var] = nlit; + //cache negations + plit->negation = nlit; + nlit->negation = plit; + //index leaf vtree + manager->leaf_vtrees[var] = v; + }); +} + +void setup_terminal_sdds(SddManager* manager) { + //constant sdds + setup_true_false_sdds(manager); + //literal sdds + setup_literal_sdds(manager->vtree,manager); +} + +/**************************************************************************************** + * freeing manager and associated structures + ****************************************************************************************/ + +//free manager and associated structures +void sdd_manager_free(SddManager* manager) { + assert(manager->stats.element_count==manager->gc_element_count+manager->sdd_size); + assert(manager->start_compression_stack==manager->top_compression_stack); + assert(manager->start_element_stack==manager->top_element_stack); + assert(manager->apply_depth==0); + assert(manager->limited_apply_depth==0); + + //true and false sdds + free_sdd_node(manager->true_sdd,manager); + free_sdd_node(manager->false_sdd,manager); + + //literal sdds + FOR_each_leaf_vtree_node(v,manager->vtree,{ + free_sdd_node(v->nodes->vtree_next,manager); //negative literal (must be freed first) + free_sdd_node(v->nodes,manager); //positive literal (must be freed second) + }) + + //unique nodes + FOR_each_unique_node(n,manager,free_sdd_node(n,manager)); //free unique nodes + free_hash(manager->unique_nodes); //free hash tables + + //node structures in gc lists + for(int i=0; igc_node_lists[i]; + FOR_each_linked_node(node,list,free_sdd_node(node,manager)); + } + free(manager->gc_node_lists); + + //computation caches + free(manager->conjoin_cache); + free(manager->disjoin_cache); + + //vtree and its associated structures + sdd_vtree_free(manager->vtree); + + //manager indices + free(manager->literals - manager->var_count); + free(manager->leaf_vtrees); + + //elements stacks + free(manager->start_compression_stack); + free(manager->start_meta_compression_stack); + free(manager->start_cp_stack1); + free(manager->start_cp_stack2); + free(manager->start_cp_stack3); + free(manager->start_element_stack); + + //node buffer + free(manager->node_buffer); + + assert(manager->stats.element_count==0); + + //manager + free(manager); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/manager/stats.c b/pysdd/lib/libsdd-2.0/src/manager/stats.c new file mode 100644 index 0000000..721045d --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/manager/stats.c @@ -0,0 +1,121 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//util.c +char* ppc(SddSize n); + +/**************************************************************************************** + * printing manager stats + ****************************************************************************************/ + +//str is a control string for printing size (in mega bytes) of allocated and gc'd nodes/elements +//str must contain four %f control +void print_manager_free_str_memory_MB(const char* str, SddManager* manager) { + //some elements may have been allocated and/or then freed by rotate or swap + assert(manager->stats.max_element_count >= manager->sdd_size+manager->gc_element_count); + float node_memory = TYPE2MB(manager->gc_node_count,SddNode); + float element_memory = TYPE2MB(manager->gc_element_count,SddElement); + printf(str,node_memory,element_memory,node_memory+element_memory); +} + +//str is a control string for printing size (in mega bytes) of allocated nodes/elements +//str must contain four %f control +void print_manager_str_memory_MB(const char* str, SddManager* manager) { + float node_memory = TYPE2MB(manager->node_count,SddNode); + float element_memory = TYPE2MB(manager->sdd_size,SddElement); + printf(str,node_memory,element_memory,node_memory+element_memory); +} + +#define DIV(a,b) ((b)==0? 0: ((a)/(b))) + +//print manager stats +void sdd_manager_print(SddManager* manager) { + + char* s1; char* s2; + printf( "\nBASIC STATS\n"); + printf( " recursed apply counts \t: %s total (%.3f%% top-level)\n",s1=ppc(manager->stats.apply_count),100.0*manager->stats.apply_count_top/manager->stats.apply_count); + free(s1); + printf( " sdd size \t: %s live, %s dead\n",s1=ppc(sdd_manager_live_size(manager)),s2=ppc(sdd_manager_dead_size(manager))); + free(s1); free(s2); + printf( " sdd count \t: %s live, %s dead\n",s1=ppc(sdd_manager_live_count(manager)),s2=ppc(sdd_manager_dead_count(manager))); + free(s1); free(s2); + printf( " largest decomposition \t: %"PRInsS" compressed, %"PRInsS" uncompressed\n",manager->stats.max_decomposition_size,manager->stats.max_uncompressed_decomposition_size); + + SddSize op_count = manager->vtree_ops.lr_count+manager->vtree_ops.rr_count+manager->vtree_ops.sw_count; + SddSize failed_time_count = manager->vtree_ops.failed_lr_count_time+manager->vtree_ops.failed_rr_count_time+manager->vtree_ops.failed_sw_count_time; + SddSize failed_size_count = manager->vtree_ops.failed_lr_count_size+manager->vtree_ops.failed_rr_count_size+manager->vtree_ops.failed_sw_count_size; + SddSize failed_memory_count = manager->vtree_ops.failed_lr_count_memory+manager->vtree_ops.failed_rr_count_memory+manager->vtree_ops.failed_sw_count_memory; + + printf( "\nVTREE OPERATIONS \t: lr rr sw\n"); + printf( " total \t: %10"PRIsS" %10"PRIsS" %10"PRIsS"\n",manager->vtree_ops.lr_count,manager->vtree_ops.rr_count,manager->vtree_ops.sw_count); + printf( " failed (time) \t: %10"PRIsS" %10"PRIsS" %10"PRIsS" (%.1f%%)\n",manager->vtree_ops.failed_lr_count_time,manager->vtree_ops.failed_rr_count_time,manager->vtree_ops.failed_sw_count_time,op_count==0?0:((100.0*failed_time_count)/op_count)); + printf( " failed (size) \t: %10"PRIsS" %10"PRIsS" %10"PRIsS" (%.1f%%)\n",manager->vtree_ops.failed_lr_count_size,manager->vtree_ops.failed_rr_count_size,manager->vtree_ops.failed_sw_count_size,op_count==0?0:((100.0*failed_size_count)/op_count)); + printf( " failed (memory) \t: %10"PRIsS" %10"PRIsS" %10"PRIsS" (%.1f%%)\n",manager->vtree_ops.failed_lr_count_memory,manager->vtree_ops.failed_rr_count_memory,manager->vtree_ops.failed_sw_count_memory,op_count==0?0:((100.0*failed_memory_count)/op_count)); + printf( " failed (cartesian prods) \t: %10"PRIsS" (rr+sw)\n",manager->vtree_ops.failed_count_cp); + + printf( "\nNODES AND ELEMENTS \t: n e\n"); + print_manager_str_memory_MB( " memory (allocated) \t:%10.1f%10.1f (%.1f MB)\n",manager); + print_manager_free_str_memory_MB( " memory (free) \t:%10.1f%10.1f (%.1f MB)\n",manager); + + SddHash* hash = manager->unique_nodes; + printf( "\nHASH TABLES \t:\n"); + printf( " nodes:\n"); + printf( " size \t:%10s (%.1f MBs, %.1f%% saturation)\n",s1=ppc(hash->size),TYPE2MB(hash->size,SddComputed*),saturation(hash)); free(s1); + printf( " hit rate \t:%10.1f%%\n",hit_rate(hash)); + printf( " ave lookup cost \t:%10.1f\n",ave_lookup_cost(hash)); + printf( " increase-size count \t:%10"PRIsS"\n",hash->increase_size_count); + printf( " decrease-size count \t:%10"PRIsS"\n",hash->decrease_size_count); + printf( " computed:\n"); + printf( " size \t:%10s (%.1f MBs)\n",s1=ppc(COMPUTED_CACHE_SIZE),TYPE2MB(2*COMPUTED_CACHE_SIZE,SddComputed)); free(s1); + printf( " hit rate \t:%10.1f%%\n",100.0*manager->computed_cache_hit_count/manager->computed_cache_lookup_count); + printf( " saturation \t:%10.1f%%\n",100.0*manager->computed_count/(2*COMPUTED_CACHE_SIZE)); + + SddManagerVtreeOps ops = manager->vtree_ops; + printf( "\nMINIMIZATION OPTIONS:\n"); + printf( " time limits \t:%10.2f secs (apply), %.1f secs (operation), %.1f secs (fragment), %.1f secs (search)\n",(double)ops.apply_time_limit/CLOCKS_PER_SEC,(double)ops.op_time_limit/CLOCKS_PER_SEC,(double)ops.fragment_time_limit/CLOCKS_PER_SEC,(double)ops.search_time_limit/CLOCKS_PER_SEC); + printf( " size limit \t:%10.1f (min: %d)\n",ops.op_size_limit,VTREE_OP_SIZE_MIN); + printf( " memory limit \t:%10.1f (min: %.1f MB)\n",ops.op_memory_limit,ops.op_memory_limit>0?VTREE_OP_MEMORY_MIN:0.0); + printf( " cartesian-product limits \t:%10"PRIsS"\n",ops.cartesian_product_limit); + printf( " convergence threshold \t:%10.1f%%\n",ops.convergence_threshold); + + int icount = manager->auto_search_invocation_count; + printf( "\nAUTO GC & MINIMIZE \t:\n"); + printf( " gc invocation count \t:%10d\n",manager->auto_gc_invocation_count); + printf( " search invocation count \t:%10d (%.1f secs)\n",icount,((float)manager->stats.auto_search_time)/CLOCKS_PER_SEC); + + if(icount) { + printf( " time per search \t:%10.1f ave secs, %.1f max secs\n",((float)manager->stats.auto_search_time)/CLOCKS_PER_SEC/icount,((float)manager->stats.auto_max_search_time)/CLOCKS_PER_SEC); + printf( " triggers \t:%10.1f%% global, %.1f%% local, %.1f%% recursive\n",100.0*manager->auto_search_invocation_count_global/icount,100.0*manager->auto_search_invocation_count_local/icount,100.0*manager->auto_search_invocation_count_recursive/icount); + printf( " average iterations \t:%10.1f\n",((float)manager->auto_search_iteration_count)/icount); + printf( " average reduction \t:%10.1f%%\n",manager->auto_search_reduction_sum/icount); + printf( " aborted searches \t:%10d apply, %d operation, %d fragment, %d search\n",manager->auto_search_invocation_count_aborted_apply,manager->auto_search_invocation_count_aborted_operation,manager->auto_search_invocation_count_aborted_fragment,manager->auto_search_invocation_count_aborted_search); + } + + printf( "\nVTREE FRAGMENTS \t:\n"); + printf( " max shadow memory \t:%10.1f MB\n",manager->max_fragment_shadow_byte_count/(1024*1024.0)); + printf( " completed \t:%8.2f%%\n",100.0*manager->completed_fragment_count/manager->fragment_count); + printf( " successful \t:%8.2f%%\n",100.0*manager->successful_fragment_count/manager->fragment_count); + if(FRAGMENT_SEARCH_BACKWARD) { + printf( " backward | completed \t:%8.2f%%\n",100.0*manager->backward_completed_fragment_count/manager->completed_fragment_count); + printf( " backward | successful \t:%8.2f%%\n",100.0*manager->backward_successful_fragment_count/manager->successful_fragment_count); + } + printf( " successful | completed \t:%8.2f%%\n",100.0*manager->successful_completed_fragment_count/manager->completed_fragment_count); + if(manager->fragment_count!=manager->completed_fragment_count) + printf( " successful | not completed\t:%8.2f%%\n",100.0*(manager->successful_fragment_count-manager->successful_completed_fragment_count)/(manager->fragment_count-manager->completed_fragment_count)); + + #ifdef NDEBUG + #else + printf("\nASSERTIONS ON! ASSERTIONS ON! ASSERTIONS ON!\n"); + #endif +} + + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/manager/variables.c b/pysdd/lib/libsdd-2.0/src/manager/variables.c new file mode 100644 index 0000000..0feaea8 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/manager/variables.c @@ -0,0 +1,267 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//basic/memory.c +void gc_sdd_node(SddNode* node, SddManager* manager); + +//manager/manager.c +void setup_literal_sdds(Vtree* vtree, SddManager* manager); + +//vtrees/vtree.c +Vtree* first_leaf_vtree(Vtree* vtree); +Vtree* last_leaf_vtree(Vtree* vtree); + +//vtrees/compare.c +Vtree* sdd_manager_lca_of_literals(int count, SddLiteral* variables, SddManager* manager); + +//vtrees/edit.c +Vtree* add_var_to_vtree(SddLiteral var, char location, Vtree* sibling, SddManager* manager); +void remove_var_from_vtree(SddLiteral var, SddManager* manager); +void move_var_in_vtree(SddLiteral var, char var_location, Vtree* new_sibling, SddManager* manager); + +/**************************************************************************************** + * a variable is "used" iff + * one of its literals appear as prime or sub of an sdd node (dead or alive) + * + * NOTE: this is a weak notion of usage since an unused variable may still have one of + * its literals assigned to some variable by the user + * + * MOVING unused variables is always safe + * + * REMOVING unused variables may not be safe: it is the user responsibility to ensure + * that no direct references (i.e., variable assignmnets) exist to its literals + ****************************************************************************************/ + +//a variable is "used" iff one of its literals appear as prime or sub in some sdd node (dead or alive) +//returns 1 if variable is used, 0 otherwise +int sdd_manager_is_var_used(SddLiteral var, SddManager* manager) { + return (sdd_manager_literal(var,manager))->parent_count > 0 || (sdd_manager_literal(-var,manager))->parent_count > 0; +} + +//returns an array map with the following properties: +//size : 1+number of variables in manager +//map[var]: 1 if var is used, 0 otherwise +//map[0] : not used +int* var_usage_map(SddManager* manager) { + int* map; + CALLOC(map,int,1+manager->var_count,"var_usage_map"); + for(int var=1; var<=manager->var_count; var++) map[var] = sdd_manager_is_var_used(var,manager); + return map; +} + +/**************************************************************************************** + * add var to a manager + * + * index of added variable is 1+largest index of variables in manager + * + * a new leaf_node (for variable) will be added as a side effect to the manager's vtree + * a new internal_node will be added as a side effect to the manager's vtree + * this new internal node will inherit the parent of sibling (if any) + * + * if location='l', internal_node will have children (leaf_node,sibling) + * if location='r', internal_node will have children (sibling,leaf_node) + * + ****************************************************************************************/ + +void add_var_to_manager(char location, Vtree* sibling, SddManager* manager) { + + SddLiteral last_var_count = manager->var_count; + SddLiteral new_var_count = ++manager->var_count; + assert(last_var_count >= 1); + + //add new leaf node to current vtree + Vtree* new_leaf = add_var_to_vtree(new_var_count,location,sibling,manager); //added leaf node + + //expand literals array to hold entries for added variable + SddLiteral last_array_size = 1+2*last_var_count; + SddLiteral new_array_size = 1+2*new_var_count; + //literal indices are arranged as follows (pointers are positioned at 0): + //initially: -last_var_count,...,-2,-1,0,+1,+2,...,+last_var_count + //expanded: -new_var_count,-last_var_count,...,-2,-1,0,+1,+2,...,+last_var_count,+new_var_count + + //position pointer at beginning of array + manager->literals -= last_var_count; + //increase size of array (by 2 cells) + REALLOC(manager->literals,SddNode*,new_array_size,"add_var_to_manager"); + //shift entries of array forward by one cell + memmove(1+manager->literals,manager->literals,last_array_size*sizeof(SddNode*)); + //position pointer back at 0 + manager->literals += new_var_count; + + //expand the index of leaf vtrees to hold an entry for the new leaf vtree + //NULL, 1, 2, ..., new_var + REALLOC(manager->leaf_vtrees,Vtree*,1+new_var_count,"add_var_to_manager"); + + //initialization for newly constructed nodes + + //construct literal sdds for new leaf node + //index new leaf vtree + setup_literal_sdds(new_leaf,manager); +} + + +// +//in all of the following functions: +// +//--index of added variable is 1+largest index of variables in manager +//--before: means left sibling of +//--after : means right sibling of + +//before the first variable in the vtree inorder +void sdd_manager_add_var_before_first(SddManager* manager) { + Vtree* sibling = first_leaf_vtree(manager->vtree); + add_var_to_manager('l',sibling,manager); +} + +//after the last variable in the vtree inorder +void sdd_manager_add_var_after_last(SddManager* manager) { + Vtree* sibling = last_leaf_vtree(manager->vtree); + add_var_to_manager('r',sibling,manager); +} + +//before the root of the vtree +void add_var_before_top(SddManager* manager) { + Vtree* sibling = manager->vtree; + add_var_to_manager('l',sibling,manager); +} + +//after the root of the vtree +void add_var_after_top(SddManager* manager) { + Vtree* sibling = manager->vtree; + add_var_to_manager('r',sibling,manager); +} + +//before var +void sdd_manager_add_var_before(SddLiteral target_var, SddManager* manager) { + Vtree* sibling = sdd_manager_vtree_of_var(target_var,manager); + add_var_to_manager('l',sibling,manager); +} + +//after var +void sdd_manager_add_var_after(SddLiteral target_var, SddManager* manager) { + Vtree* sibling = sdd_manager_vtree_of_var(target_var,manager); + add_var_to_manager('r',sibling,manager); +} + +//variables is an array of size count containing the variables +//before the least common ancestor (lca) of the given variables +void add_var_before_lca(int count, SddLiteral* variables, SddManager* manager) { + Vtree* sibling = sdd_manager_lca_of_literals(count,variables,manager); + add_var_to_manager('l',sibling,manager); +} + +//variables is an array of size count containing the variables +//after the least common ancestor (lca) of the given variables +void add_var_after_lca(int count, SddLiteral* variables, SddManager* manager) { + Vtree* sibling = sdd_manager_lca_of_literals(count,variables,manager); + add_var_to_manager('r',sibling,manager); +} + + +/**************************************************************************************** + * moving var in a manager + * + * assumes: + * --the manager vtree has at least two variables + * --the variable is not used + * + ****************************************************************************************/ + +// +//in all of the following functions: +// +//--before: means left sibling of +//--after : means right sibling of + +//before first variable in vtree inorder +void move_var_before_first(SddLiteral var, SddManager* manager) { + Vtree* sibling = first_leaf_vtree(manager->vtree); + move_var_in_vtree(var,'l',sibling,manager); +} + +//after last variable in vtree inorder +void move_var_after_last(SddLiteral var, SddManager* manager) { + Vtree* sibling = last_leaf_vtree(manager->vtree); + move_var_in_vtree(var,'r',sibling,manager); +} + +//before target_var +void move_var_before(SddLiteral var, SddLiteral target_var, SddManager* manager) { + Vtree* sibling = sdd_manager_vtree_of_var(target_var,manager); + move_var_in_vtree(var,'l',sibling,manager); +} + +//after target_var +void move_var_after(SddLiteral var, SddLiteral target_var, SddManager* manager) { + Vtree* sibling = sdd_manager_vtree_of_var(target_var,manager); + move_var_in_vtree(var,'r',sibling,manager); +} + + +/**************************************************************************************** + * remove last var from manager + * + * index of removed variable is largest index of variables in manager + * + * assumes: + * --variable is not used + * --manager has at least two variables + * + ****************************************************************************************/ + +void remove_var_added_last(SddManager* manager) { + + CHECK_ERROR(manager->var_count<=1,ERR_MSG_TWO_VARS,"remove_last_var"); + CHECK_ERROR(sdd_manager_is_var_used(manager->var_count,manager),ERR_MSG_REM_VAR,"remove_last_var"); + + SddLiteral last_var_count = manager->var_count; + SddLiteral new_var_count = --manager->var_count; + assert(new_var_count!=0); + + //nodes to be removed from vtree + Vtree* removed_leaf = sdd_manager_vtree_of_var(last_var_count,manager); + assert(removed_leaf->parent); + assert(removed_leaf->parent->nodes==NULL); + + //gc literal sdds for removed leaf (var) + //these are not "freed" as they may still exist in some computed cache and, hence, the + //test INVALID_COMPUTED may end up accessing them + gc_sdd_node(removed_leaf->nodes->vtree_next,manager); //negative literal (must be gc'd first) + gc_sdd_node(removed_leaf->nodes,manager); //positive literal (must be gc'd second) + + //remove leaf node (of last variable) and corresponding internal node from vtree + remove_var_from_vtree(last_var_count,manager); + + //shrink literals array to delete entries for removed variable + + SddLiteral new_array_size = 1+2*new_var_count; + //literal indices are arranged as follows (pointers are positioned at 0): + //initially: -last_var_count,...,-2,-1,0,+1,+2,...,+last_var_count + //shrunk : -new_var_count ,...,-2,-1,0,+1,+2,...,+new_var_count + + //position pointer at beginning of array + manager->literals -= last_var_count; + //shift entries of array one cell backwards + memmove(manager->literals,1+manager->literals,new_array_size*sizeof(SddNode*)); + //shrink size of array (by 2 cells) + REALLOC(manager->literals,SddNode*,new_array_size,"remove_last_var"); + //reposition pointer at 0 + manager->literals += new_var_count; + + //shrink the index of leaf vtrees to delete the entry for the removed leaf vtree + //NULL, 1, 2, ..., last_var-1 + REALLOC(manager->leaf_vtrees,Vtree*,1+new_var_count,"remove_last_var"); + +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/apply.c b/pysdd/lib/libsdd-2.0/src/sdds/apply.c new file mode 100644 index 0000000..8c4a912 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/apply.c @@ -0,0 +1,396 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/multiply.c +int multiply_decompositions(SddElement* elements1, SddNodeSize size1, SddElement* elements2, SddNodeSize size2, + BoolOp op, Vtree* vtree, SddManager* manager, int limited, void fn(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager* manager)); + +//vtree_operations/limits.c +void start_apply_limits(SddManager* manager); +void end_apply_limits(SddManager* manager); +int apply_aborted(SddManager* manager); +int exceeded_limits(SddManager* manager); + +//vtree_search/auto.c +void try_auto_gc_and_minimize(Vtree* vtree, SddManager* manager); + +//vtrees/compare.c +char cmp_vtrees(Vtree** lca, Vtree* vtree1, Vtree* vtree2); + +//local declarations +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited); + +/**************************************************************************************** + * macros for protecting nodes + ****************************************************************************************/ + +#define c_ref(N,M) if(M->auto_gc_and_search_on) sdd_ref(N,M) +#define c_deref(N,M) if(M->auto_gc_and_search_on) sdd_deref(N,M) +//#define c_ref(N,M) +//#define c_deref(N,M) + +/**************************************************************************************** + * apply + * + * suppose node is a decomposition normalized for vtree + * + * invariants: + * --node cannot have true prime (trimming) + * --node cannot have all its subs as either true or false (trimming) + * --all variables of node appear in vtree + * --no subtree of vtree satisfies above property + * + ****************************************************************************************/ + +//node1 and node2 are normalized for arbitrary vtrees +//no time limits +SddNode* sdd_apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager) { + assert(node1!=NULL && node2!=NULL); + CHECK_ERROR(GC_NODE(node1),ERR_MSG_GC,"sdd_apply"); + CHECK_ERROR(GC_NODE(node2),ERR_MSG_GC,"sdd_apply"); + + SddNode* node = apply(node1,node2,op,manager,0); + assert(node!=NULL); + return node; +} + +/**************************************************************************************** + * negate + ****************************************************************************************/ + +//node is normalized for an arbitrary vtree +SddNode* sdd_negate(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_negate"); + + SddNode* negation = node->negation; + if(negation!=NULL) { + assert(!GC_NODE(negation)); + assert(node->vtree==negation->vtree); + assert(node==negation->negation); + return negation; + } + + assert(node->type==DECOMPOSITION); + Vtree* vtree = node->vtree; + + //Note: compression is not possible here + GET_node_from_compressed_partition(negation,vtree,manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + SddNode* sub_neg = sdd_negate(sub,manager); + DECLARE_compressed_element(prime,sub_neg,vtree,manager); + }); + }); + assert(negation); + assert(node->vtree==negation->vtree); + + //cache negations + node->negation = negation; + negation->negation = node; + + return negation; +} + +/**************************************************************************************** + * apply case 1: node1->vtree = node2->vtree + ****************************************************************************************/ + +//node1 and node2 are normalized for vtree (node1->vtree = vtree and node2->vtree = vtree) +//result is normalized for vtree unless it was trimmed +static +SddNode* sdd_apply_equal(SddNode* node1, SddNode* node2, BoolOp op, Vtree* vtree, SddManager* manager, int limited) { + assert(node1!=NULL && node2!=NULL); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + assert(node1->vtree==vtree); + assert(node1->vtree==vtree); + + c_ref(node1,manager); c_ref(node2,manager); //protect + + SddNode* node; + //NOTE: compression is possible here (i.e., apply may be called) + GET_node_from_partition_limited(node,vtree,manager,limited,{ + int success = + multiply_decompositions( + ELEMENTS_OF(node1),node1->size, + ELEMENTS_OF(node2),node2->size, + op,vtree,manager,limited, + DECLARE_element); + ABORT_partition_if(success==0); + }); + + c_deref(node1,manager); c_deref(node2,manager); //release + + return node; //could be NULL +} + +/**************************************************************************************** + * apply case 2: node1->vtree in node2->vtree->left + ****************************************************************************************/ + +//node2 is normalized for vtree (node2->vtree = vtree) +//node1 is normalized for a sub_vtree of vtree->left +//result is normalized for vtree unless it was trimmed +static +SddNode* sdd_apply_left(SddNode* node1, SddNode* node2, BoolOp op, Vtree* vtree, SddManager* manager, int limited) { + assert(node1!=NULL && node2!=NULL); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + assert(node1->vtree->position < node2->vtree->position); + assert(node2->vtree==vtree); + assert(sdd_vtree_is_sub(node1->vtree,vtree->left)); + + SddNode* node1_neg = sdd_negate(node1,manager); + SddNode* n = (op==CONJOIN? node1: node1_neg); + + c_ref(n,manager); c_ref(node2,manager); //protect + + SddNode* node; + GET_node_from_partition_limited(node,vtree,manager,limited,{ + DECLARE_element(n->negation,ZERO(manager,op),vtree,manager); + FOR_each_prime_sub_of_node(prime,sub,node2,{ + //vtree will not change, but vtree->left may change + SddNode* new_prime = apply(prime,n,CONJOIN,manager,limited); //could be NULL + ABORT_partition_if(new_prime==NULL); + if(!IS_FALSE(new_prime)) DECLARE_element(new_prime,sub,vtree,manager); + }); + }); + + c_deref(n,manager); c_deref(node2,manager); //release + + return node; //could be NULL +} + +/**************************************************************************************** + * apply case 3: node2->vtree in node1->vtree->right + ****************************************************************************************/ + +//node1 is normalized for vtree (node1->vtree = vtree) +//node2 is normalized for a sub_vtree of vtree->right +//result is normalized for vtree unless it was trimmed +static +SddNode* sdd_apply_right(SddNode* node1, SddNode* node2, BoolOp op, Vtree* vtree, SddManager* manager, int limited) { + assert(node1!=NULL && node2!=NULL); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + assert(node1->vtree->position < node2->vtree->position); + assert(node1->vtree==vtree); + assert(sdd_vtree_is_sub(node2->vtree,vtree->right)); + + c_ref(node1,manager); c_ref(node2,manager); //protect + + SddNode* node; + //NOTE: compression is possible here (i.e., apply may be called) + GET_node_from_partition_limited(node,vtree,manager,limited,{ + FOR_each_prime_sub_of_node(prime,sub,node1,{ + //vtree will not change, but vtree->right may change + SddNode* new_sub = apply(sub,node2,op,manager,limited); //could be NULL + ABORT_partition_if(new_sub==NULL); + DECLARE_element(prime,new_sub,vtree,manager); + }); + }); + + c_deref(node1,manager); c_deref(node2,manager); //release + + return node; //could be NULL +} + +/**************************************************************************************** + * apply case 4: node1->vtree and node2->vtree incomparable + ****************************************************************************************/ + +//node1 and node2 are normalized for incomparable vtree nodes +//node1 is before node2 in the vtree inorder +//vtree is lca of node1->vtree and node2->vtree +//result is normalized for vtree +static +SddNode* sdd_apply_incomparable(SddNode* node1, SddNode* node2, BoolOp op, Vtree* vtree, SddManager* manager, int limited) { + assert(node1!=NULL && node2!=NULL); + assert(NON_TRIVIAL(node1) && NON_TRIVIAL(node2)); + assert(node1->vtree->position < node2->vtree->position); + assert(sdd_vtree_is_sub(node1->vtree,vtree)); + assert(sdd_vtree_is_sub(node2->vtree,vtree)); + assert(!sdd_vtree_is_sub(node1->vtree,node2->vtree)); + assert(!sdd_vtree_is_sub(node2->vtree,node1->vtree)); + + SddNode* node1_neg = sdd_negate(node1,manager); + + SddNode* node1_sub = apply(node2,manager->true_sdd,op,manager,limited); + assert(node1_sub!=NULL); + + SddNode* node1_neg_sub = apply(node2,manager->false_sdd,op,manager,limited); + assert(node1_neg_sub!=NULL); + + SddNode* node; + //NOTE: no compression is possible here (i.e., apply will not be called) + GET_node_from_compressed_partition(node,vtree,manager,{ + DECLARE_compressed_element(node1,node1_sub,vtree,manager); + DECLARE_compressed_element(node1_neg,node1_neg_sub,vtree,manager); + }); + + assert(node!=NULL); + return node; +} + +/**************************************************************************************** + * apply master: channel to one of the above cases + ****************************************************************************************/ + +//checks whether the current apply call has no parent apply calls +int root_apply(SddManager* manager) { + return manager->apply_depth==1; +} + +//checks whether the current apply call has no limited, parent apply calls +static inline +int root_limited_apply(SddManager* manager) { + return manager->limited_apply_depth==1; +} + +//book keeping when starting an apply that could invoke vtree search +static inline +void prepare_for_vtree_search(Vtree* lca, SddManager* manager) { + manager->auto_apply_vtree = lca; + manager->auto_apply_outside_live_size = (manager->sdd_size-manager->dead_sdd_size); + manager->auto_apply_outside_live_count = (manager->node_count-manager->dead_node_count); + manager->auto_apply_outside_dead_count = manager->dead_node_count; + FOR_each_internal_vtree_node(v,lca,{ + manager->auto_apply_outside_live_size -= (v->sdd_size-v->dead_sdd_size); + manager->auto_apply_outside_live_count -= (v->node_count-v->dead_node_count); + manager->auto_apply_outside_dead_count -= v->dead_node_count; + }); +} + +//not limited: vtree search possible +static +SddNode* u_apply(char apply_type, Vtree* lca, SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager) { + if(manager->auto_gc_and_search_on && root_apply(manager)) prepare_for_vtree_search(lca,manager); + SddNode* node = NULL; + switch(apply_type) { + case 'e': node = sdd_apply_equal(node1,node2,op,lca,manager,0); break; + case 'l': node = sdd_apply_left(node1,node2,op,lca,manager,0); break; + case 'r': node = sdd_apply_right(node1,node2,op,lca,manager,0); break; + case 'i': node = sdd_apply_incomparable(node1,node2,op,lca,manager,0); break; + default: assert(0); + } + assert(node); + cache_computation(node1,node2,node,op,manager); + if(manager->auto_gc_and_search_on && lca->var_count > 1) { + sdd_ref(node,manager); //same as c_ref + try_auto_gc_and_minimize(lca,manager); + sdd_deref(node,manager); //same as c_deref + } + return node; +} + +//limited: vtree search not possible +static +SddNode* l_apply(char apply_type, Vtree* lca, SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager) { + ++manager->limited_apply_depth; + if(root_limited_apply(manager)) start_apply_limits(manager); + SddNode* node = NULL; + switch(apply_type) { + case 'e': node = sdd_apply_equal(node1,node2,op,lca,manager,1); break; + case 'l': node = sdd_apply_left(node1,node2,op,lca,manager,1); break; + case 'r': node = sdd_apply_right(node1,node2,op,lca,manager,1); break; + case 'i': node = sdd_apply_incomparable(node1,node2,op,lca,manager,1); break; + default: assert(0); + } + if(node) { + cache_computation(node1,node2,node,op,manager); + //only place where limits are checked (but see search.c where result of check are examined) + if(exceeded_limits(manager)) node = NULL; //failed + } + if(root_limited_apply(manager)) end_apply_limits(manager); + --manager->limited_apply_depth; + return node; +} + +//node1 and node2 are normalized for arbitrary vtrees +//node1->vtree and node2->vtree are sub_vtrees of vtree +//result is normalized for a sub_vtree of vtree +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited) { + assert(!apply_aborted(manager)); + assert(node1!=NULL && node2!=NULL); + assert(!GC_NODE(node1)); + assert(!GC_NODE(node2)); + + //base cases + if(node1==node2) return node1; + if(node1==node2->negation) return ZERO(manager,op); //same as ZERO(node2->vtree,op); + if(IS_ZERO(node1,op) || IS_ZERO(node2,op)) return ZERO(manager,op); + if(IS_ONE(node1,op)) return node2; + if(IS_ONE(node2,op)) return node1; + + //check cache + SddNode* node = lookup_computation(node1,node2,op,manager); + if(node!=NULL) return node; //cache hit + + //cache miss: must recurse + ++manager->apply_depth; + + ++manager->stats.apply_count; //all recursed applies + if(root_apply(manager)) ++manager->stats.apply_count_top; //top-level recursed applies + + //order is required by cmp_vtrees and recursive apply calls + if(node1->vtree->position > node2->vtree->position) SWAP(SddNode*,node1,node2); + Vtree* lca = NULL; //lowest common ancestor + char apply_type = cmp_vtrees(&lca,node1->vtree,node2->vtree); + //(ab) + (a~b) = a, which is why node->vtree!=lca in general + + node = limited? l_apply(apply_type,lca,node1,node2,op,manager): + u_apply(apply_type,lca,node1,node2,op,manager); + + --manager->apply_depth; + return node; +} + +/**************************************************************************************** + * special case of sdd_apply used by left/right rotations + * + * could be handled by sdd_apply but this saves some overhead as it is called a lot + * (it will also not call vtree search) + * + * assumes: + * --node1->vtree and node2->vtree are incomparable + * --node1->vtree->position < node2->vtree->position + * --vtree contains node1->vtree and node2->vtree + ****************************************************************************************/ + +//not limited +SddNode* sdd_conjoin_lr(SddNode* node1, SddNode* node2, Vtree* lca, SddManager* manager) { + assert(!apply_aborted(manager)); + assert(node1!=NULL && node2!=NULL); + assert(!GC_NODE(node1)); + assert(!GC_NODE(node2)); + + if(IS_FALSE(node1) || IS_FALSE(node2)) return manager->false_sdd; + if(IS_TRUE(node1)) return node2; + if(IS_TRUE(node2)) return node1; + + assert(INTERNAL(lca)); + assert(sdd_vtree_is_sub(node1->vtree,lca->left)); + assert(sdd_vtree_is_sub(node2->vtree,lca->right)); + + ++manager->apply_depth; + ++manager->stats.apply_count; //this is considered a recursed apply + + SddNode* node = lookup_computation(node1,node2,CONJOIN,manager); + if(node==NULL) { //no compression or trimming possible + GET_node_from_compressed_partition(node,lca,manager,{ + DECLARE_compressed_element(node1,node2,lca,manager); + DECLARE_compressed_element(sdd_negate(node1,manager),manager->false_sdd,lca,manager); + }); + cache_computation(node1,node2,node,CONJOIN,manager); + } + + assert(node); + --manager->apply_depth; + return node; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/bits.c b/pysdd/lib/libsdd-2.0/src/sdds/bits.c new file mode 100644 index 0000000..5bae4e2 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/bits.c @@ -0,0 +1,156 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * each sdd node has two associated bits: bit and cit + * + * initially all bits/cits are cleared + * every algorithm that uses bits and cits must maintain this invariant after being done + ****************************************************************************************/ + +//clear bits and cits of sdd nodes +void sdd_clear_node_bits(SddNode* node) { + if(node->bit==0) return; //cit must be 0 + node->bit=0; + node->cit=0; //cit may have been 0 or 1 + if(IS_DECOMPOSITION(node)) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_clear_node_bits(prime); + sdd_clear_node_bits(sub); + }); + } +} + + +/**************************************************************************************** + * constructs an array of all nodes in the sdd, with children appearing before parents + * + * sets the index of each node to its location in the array + ****************************************************************************************/ + +SddNode** sdd_topological_sort(SddNode* node, SddSize* size) { + + void sdd_topological_sort_aux(SddNode* node, SddNode** start, SddNode*** array); + + //count number of nodes in sdd + *size = sdd_all_node_count_leave_bits_1(node); + //all nodes are marked 1 now + + //allocate array to hold sdd nodes + SddNode** array; + CALLOC(array,SddNode*,*size,"sdd_topological_sort"); + + //fill array + sdd_topological_sort_aux(node,array,&array); + //all nodes are marked 0 now + //array is now pointing to last cell in allocated array + + return array-*size+1; +} + + +void sdd_topological_sort_aux(SddNode* node, SddNode** start, SddNode*** array) { + + if(node->bit==0) { //node has been visited before + --(*array); + return; + } + //this is the first visit to this node + node->bit = 0; + + if(IS_DECOMPOSITION(node)) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_topological_sort_aux(prime,start,array); + (*array)++; + sdd_topological_sort_aux(sub,start,array); + (*array)++; + }); + } + + **array = node; //save node + node->index = *array-start; //save location of node +} + + +/**************************************************************************************** + * count of sdd nodes having more than one parent + * + * LEAVES bits of counted nodes set to 1 + ****************************************************************************************/ + +//count the number of nodes in an sdd that have more than one parent but leaves bits set to 1 +// +//we have the following cases: +//bit cit +//0 0 node has not been visited (initial state) +//1 0 node visited only once before +//1 1 node visited at least twice before (has more than one parent) +//0 1 not used +SddSize sdd_count_multiple_parent_nodes(SddNode* node) { + if (node->bit==1) { //node visited before, it has multiple parents + if(node->cit==0) { //this is the second visit to this node + node->cit=1; + return 1; //count this node + } + else { //this has been visited at least twice before + return 0; //do not count this node + } + } + //this is the very first visit for this node + node->bit=1; + + SddSize count=0; + if(IS_DECOMPOSITION(node)) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + count += sdd_count_multiple_parent_nodes(prime); + count += sdd_count_multiple_parent_nodes(sub); + }); + } + return count; +} + +/**************************************************************************************** + * count of sdd nodes having more than one parent, visiting only sdd nodes that are + * normalized for vnodes on the path to leaf + * + * LEAVES bits of counted nodes set to 1 + ****************************************************************************************/ + +SddSize sdd_count_multiple_parent_nodes_to_leaf(SddNode* node, Vtree* leaf) { + if(node->bit==1) { //node visited before, it has multiple parents + if(node->cit==0) { //this is the second visit to this node + node->cit=1; + return 1; //count this node + } + else { //this has been visited at least twice before + return 0; //do not count this node + } + } + //this is the very first visit for this node + node->bit=1; + + SddSize count=0; + if(IS_DECOMPOSITION(node)) { + if(sdd_vtree_is_sub(leaf,node->vtree->left)) { //visit primes + FOR_each_prime_of_node(prime,node,{ + count += sdd_count_multiple_parent_nodes_to_leaf(prime,leaf); + }); + } + else if(sdd_vtree_is_sub(leaf,node->vtree->right)) { //visit subs + FOR_each_sub_of_node(sub,node,{ + count += sdd_count_multiple_parent_nodes_to_leaf(sub,leaf); + }); + } + } + + return count; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/cardinality.c b/pysdd/lib/libsdd-2.0/src/sdds/cardinality.c new file mode 100644 index 0000000..9cb983c --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/cardinality.c @@ -0,0 +1,313 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static void sdd_minimum_cardinality_aux(SddNode* node, SddLiteral* min_cards, SddLiteral** min_cards_loc); +static void sdd_minimize_cardinality_aux(SddNode* node, SddLiteral* min_cards, int* minimize_bits, SddNode** minimized_nodes, SddNode*** minimized_nodes_loc, SddManager* manager); +static SddNode* negative_term(Vtree* vtree, SddManager* manager); +static SddNode* add_negative_term(SddNode* node, Vtree* vtree, SddManager* manager); + +/**************************************************************************************** + * minimum cardinality + * + * cardinality of a model is the number of variables set to true in the model + * minimum cardinality of an sdd: is the minimum cardinality attained by any of its models + * + * Note: model is defined over the variables appearing in the given sdd + ****************************************************************************************/ + +//returns the minimum cardinality of an sdd +//returns -1 for false sdd +SddLiteral sdd_minimum_cardinality(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_minimum_cardinality"); + assert(!GC_NODE(node)); + + //find how many nodes + SddSize size = sdd_all_node_count_leave_bits_1(node); + //all nodes are marked 1 now + + //create array to hold minimum cardinality of sdd nodes + SddLiteral* min_cards; + CALLOC(min_cards,SddLiteral,size,"sdd_minimum_cardinality"); + + //compute minimum cardinalities + sdd_minimum_cardinality_aux(node,min_cards,&min_cards); + //all nodes are marked 0 now + min_cards -= size; + + SddLiteral minimum_cardinality = min_cards[node->index]; + + free(min_cards); + + return minimum_cardinality; +} + + +//computes minimum cardinalities and stores results in min_cards array +void sdd_minimum_cardinality_aux(SddNode* node, SddLiteral* min_cards, SddLiteral** min_cards_loc) { + + if(node->bit==0) return; //node has been visited before + else node->bit=0; //this is the first visit to this node + + SddLiteral min_card; + + if(node->type==FALSE) min_card = -1; //infinity + else if(node->type==TRUE) min_card = 0; + else if(node->type==LITERAL && LITERAL_OF(node) > 0) min_card = 1; + else if(node->type==LITERAL && LITERAL_OF(node) < 0) min_card = 0; + else { //decomposition + min_card = -1; //infinity + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls + sdd_minimum_cardinality_aux(prime,min_cards,min_cards_loc); + sdd_minimum_cardinality_aux(sub,min_cards,min_cards_loc); + //update minimum cardinality + if(min_cards[sub->index]!=-1) { //false sub + SddLiteral element_min_card = min_cards[prime->index]+min_cards[sub->index]; + if(min_card==-1 || element_min_card < min_card) min_card = element_min_card; + } + }); + } + + //cache computed value + **min_cards_loc = min_card; + + //save location of cached value + node->index = *min_cards_loc-min_cards; + + //move to next cell in array + ++(*min_cards_loc); + +} + +//mark each node that is part of an element having minimum cardinality in some decomposition +void mark_nodes_needing_minimization(SddNode* node, SddLiteral* min_cards, int* minimize_bits, int** minimize_bits_loc) { + + if(node->bit==1) return; //node has been visited before + else node->bit=1; //this is the first visit to this node + + if(node->type==DECOMPOSITION) { + SddLiteral min_card = min_cards[node->index]; + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls + mark_nodes_needing_minimization(prime,min_cards,minimize_bits,minimize_bits_loc); + mark_nodes_needing_minimization(sub,min_cards,minimize_bits,minimize_bits_loc); + //check + if(min_cards[sub->index]!=-1) { //false sub + SddLiteral element_min_card = min_cards[prime->index]+min_cards[sub->index]; + if(element_min_card==min_card) { + //this element will need to be minimized + minimize_bits[prime->index] = 1; + minimize_bits[sub->index] = 1; + } + } + }); + } + + //move to next cell in array + ++(*minimize_bits_loc); + +} + +/**************************************************************************************** + * minimize cardinality + * + * generate an sdd which has only minimum cardinality models + * + * Note: minimization is done with respect to the variables that appear in the node + * + ****************************************************************************************/ + +SddNode* sdd_minimize_cardinality(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_minimize_cardinality"); + assert(!GC_NODE(node)); + + if(IS_FALSE(node) || IS_TRUE(node)) return node; + + //mark vtree nodes depending on whether their variables appear in the sdd + set_sdd_variables(node,manager); + + //find how many nodes + SddSize size = sdd_all_node_count_leave_bits_1(node); + //all nodes are marked 1 now + + //create array to hold minimum cardinality of sdd nodes + SddLiteral* min_cards; + CALLOC(min_cards,SddLiteral,size,"sdd_minimize_cardinality"); + + //create array to hold bits of sdd nodes needing minimization + int* minimize_bits; + CALLOC(minimize_bits,int,size,"sdd_minimize_cardinality"); + + //create array to hold minimizations of sdd nodes + SddNode** minimized_nodes; + CALLOC(minimized_nodes,SddNode*,size,"sdd_minimize_cardinality"); + + //compute minimum cardinalities + sdd_minimum_cardinality_aux(node,min_cards,&min_cards); + //all nodes are marked 0 now + min_cards -= size; + + //mark nodes needing minimization + mark_nodes_needing_minimization(node,min_cards,minimize_bits,&minimize_bits); + //all nodes marked 1 now + minimize_bits -= size; + minimize_bits[node->index] = 1; //root needs minimization + + //minimize + SddNode* minimized_node; + WITH_no_auto_mode(manager,{ + sdd_minimize_cardinality_aux(node,min_cards,minimize_bits,minimized_nodes,&minimized_nodes,manager); + //all nodes are maked 0 now + minimized_nodes -= size; + minimized_node = minimized_nodes[node->index]; + }); + + free(min_cards); + free(minimized_nodes); + free(minimize_bits); + + return minimized_node; +} + + +//compute minimized sdds and their carries and store in corresponding arrays +void sdd_minimize_cardinality_aux(SddNode* node, SddLiteral* min_cards, int* minimize_bits, SddNode** minimized_nodes, SddNode*** minimized_nodes_loc, SddManager* manager) { + + if(node->bit==0) return; //node has been visited before + else node->bit=0; //this is the first visit to this node + + SddNode* minimized_node = NULL; + + if(node->type==FALSE) minimized_node = sdd_manager_false(manager); + else if(node->type==TRUE) assert(1); //nothing to do here + else if(node->type==LITERAL) minimized_node = node; + else { //decomposition + + //recursive calls + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_minimize_cardinality_aux(prime,min_cards,minimize_bits,minimized_nodes,minimized_nodes_loc,manager); + sdd_minimize_cardinality_aux(sub,min_cards,minimize_bits,minimized_nodes,minimized_nodes_loc,manager); + }); + + if(minimize_bits[node->index]) { + //node is part of an element that has minimum cardinality in some decomposition node + + SddLiteral min_card = min_cards[node->index]; //already computed + Vtree* vtree = node->vtree; + + //compute minimization + GET_node_from_partition(minimized_node,vtree,manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + SddSize pi = prime->index; + SddSize si = sub->index; + + if(min_cards[si]==-1 || min_cards[pi]+min_cards[si] > min_card) { + DECLARE_element(prime,sdd_manager_false(manager),vtree,manager); + } + else { + SddNode* minimized_prime = add_negative_term(minimized_nodes[pi],vtree->left,manager); + SddNode* minimized_sub = (IS_TRUE(sub)? negative_term(vtree->right,manager): + add_negative_term(minimized_nodes[si],vtree->right,manager)); + + DECLARE_element(minimized_prime,minimized_sub,vtree,manager); + + SddNode* neg_minimized_prime = sdd_negate(minimized_prime,manager); + SddNode* prime_carry = sdd_apply(prime,neg_minimized_prime,CONJOIN,manager); + + if(!IS_FALSE(prime_carry)) DECLARE_element(prime_carry,sdd_manager_false(manager),vtree,manager); + } + }); + }); + } + + } + + //cache computed values + **minimized_nodes_loc = minimized_node; //may be NULL + + //move to next cell in array + ++(*minimized_nodes_loc); + +} + +// minimize cardinality relative to all variables, in contrast to used +// variables as in sdd_minimize_cardinality +SddNode* sdd_global_minimize_cardinality(SddNode* node, SddManager* manager) { + if(node->type==FALSE) return sdd_manager_false(manager); + SddNode* minimized_node = sdd_minimize_cardinality(node,manager); + + int* vars = sdd_variables(node,manager); + SddLiteral var_count = sdd_manager_var_count(manager); + SddNode* term = sdd_manager_true(manager); + WITH_no_auto_mode(manager,{ + for (SddLiteral var = 1; var <= var_count; var++) + if ( vars[var] == 0 ) { // unused var, include negative literal + term = sdd_apply(term,sdd_manager_literal(-var,manager),CONJOIN,manager); + } + }); + free(vars); + minimized_node = sdd_apply(minimized_node,term,CONJOIN,manager); + + return minimized_node; +} + + +/**************************************************************************************** + * computing term sdds over vtree variables that appear in the sdd being minimized + * + * these are needed to handle + * --true subs: minimization depends on where they appear (in which decomposition node) + * --primes not normalized for vtree->left: gap between vtree->right and sub->vtree + * --subs not normalized for vtree->right: gap between vtree->right and sub->vtree + * + ****************************************************************************************/ + +//returns a term (sdd) corresponding to the negative literals of sdd variables in vtree +SddNode* negative_term(Vtree* vtree, SddManager* manager) { + if(vtree->no_var_in_sdd) return sdd_manager_true(manager); + else if(LEAF(vtree)) return sdd_manager_literal(-(vtree->var),manager); + else { + SddNode* left = negative_term(vtree->left,manager); + SddNode* right = negative_term(vtree->right,manager); + return sdd_apply(left,right,CONJOIN,manager); + } +} + +//returns a term (sdd) corresponding to the ngative literals of sdd variables that appear in vtree but not in sub_vtree +SddNode* gap_negative_term(Vtree* vtree, Vtree* sub_vtree, SddManager* manager) { + if(vtree==sub_vtree) return sdd_manager_true(manager); + + SddNode* left; + SddNode* right; + if(sdd_vtree_is_sub(sub_vtree,vtree->left)) { + left = gap_negative_term(vtree->left,sub_vtree,manager); + right = negative_term(vtree->right,manager); + } + else { + left = negative_term(vtree->left,manager); + right = gap_negative_term(vtree->right,sub_vtree,manager); + } + return sdd_apply(left,right,CONJOIN,manager); + +} + +//conjoins node with a term (sdd) corresponding to the negative literals of sdd variables that +//appear in vtree but not in node->vtree +//assumes node->vtree is in vtree +SddNode* add_negative_term(SddNode* node, Vtree* vtree, SddManager* manager) { + if(IS_FALSE(node) || vtree==node->vtree) return node; + else { + SddNode* gap = gap_negative_term(vtree,node->vtree,manager); + return sdd_apply(gap,node,CONJOIN,manager); + } +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/condition.c b/pysdd/lib/libsdd-2.0/src/sdds/condition.c new file mode 100644 index 0000000..155c477 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/condition.c @@ -0,0 +1,107 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static SddNode* sdd_condition_aux(SddNode* node, SddNode* literal, SddNode** start, SddNode*** cond_nodes, SddManager* manager); + +/**************************************************************************************** + * conditioning an sdd + * + * will not do auto gc/minmize as its computations are done in no-auto mode + ****************************************************************************************/ + +//condition sdd node on literal +SddNode* sdd_condition(SddLiteral lit, SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_condition"); + + if(node->type==FALSE || node->type==TRUE) return node; + + SddNode* literal = sdd_manager_literal(lit,manager); //literal being conditioned on + + //find how many sdd nodes have multiple parents + //count only nodes that are normalized for vnodes on the path from node->vtree to leaf of literal + SddSize count = sdd_count_multiple_parent_nodes_to_leaf(node,literal->vtree); + //all relevant nodes are marked 1 now + + //create array to hold conditioned sdd nodes + SddNode** cond_nodes; + CALLOC(cond_nodes,SddNode*,count,"sdd_condition"); + + SddNode* cond_node; + WITH_no_auto_mode(manager,{ + cond_node = sdd_condition_aux(node,literal,cond_nodes,&cond_nodes,manager); + //all relevant nodes are maked 0 now + }); + + //cond_nodes is now pointing to last cell in allocated array + free(cond_nodes-count+1); + + return cond_node; +} + +//conditions node and some of its descendants on literal (if node's bit is 1) +//stores conditionings in the cond_nodes array, children before parents +//returns the conditioning of node +SddNode* sdd_condition_aux(SddNode* node, SddNode* literal, SddNode** start, SddNode*** cond_nodes, SddManager* manager) { + //leaf_position is position of the leaf vnode that hosts the variable of literal + if(node->bit==0) { //node has been visited before, lookup its cached conditioning + --(*cond_nodes); + return start[node->index]; + } + //this is the first visit to this node + node->bit=0; + + SddNode* cond_node; + Vtree* vtree = node->vtree; //could be NULL + Vtree* leaf_vtree = literal->vtree; + + if(node->type==TRUE) cond_node = node; + else if(node->type==FALSE) cond_node = node; + else if(node->type==LITERAL) { + if(leaf_vtree==vtree) { //two literals over same variable + cond_node = (node==literal? manager->true_sdd: manager->false_sdd); + } + else cond_node = node; + } + else { //decomposition node + if(sdd_vtree_is_sub(leaf_vtree,vtree->left)) { //condition primes + GET_node_from_partition(cond_node,vtree,manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + //prime cannot be true or false + SddNode* cond_prime = sdd_condition_aux(prime,literal,start,cond_nodes,manager); + (*cond_nodes)++; + if(!IS_FALSE(cond_prime)) DECLARE_element(cond_prime,sub,vtree,manager); + }); + }); + } + else if(sdd_vtree_is_sub(leaf_vtree,vtree->right)) { //condition subs + GET_node_from_partition(cond_node,vtree,manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + SddNode* cond_sub = sdd_condition_aux(sub,literal,start,cond_nodes,manager); + (*cond_nodes)++; + DECLARE_element(prime,cond_sub,vtree,manager); + }); + }); + } + else cond_node = node; //node and literal normalized for incomparable nodes + } + + if(node->cit==1) { //node has multiple parents: save its conditioning as it will be visited again + node->cit=0; + **cond_nodes = cond_node; //saving conditioning + node->index = *cond_nodes-start; //location of saved conditioning + } + else --(*cond_nodes); //node will not be visited again, do not save conditioning + + return cond_node; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/copy.c b/pysdd/lib/libsdd-2.0/src/sdds/copy.c new file mode 100644 index 0000000..c909843 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/copy.c @@ -0,0 +1,117 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static void sdd_copy_aux(SddNode* node, SddNode** start, SddNode*** node_copies_loc, Vtree* org_vtree, Vtree* dest_vtree, SddManager* dest_manager); + +/**************************************************************************************** + * copy an sdd from one manager to another + * + * will not do auto gc/minmize as its computations are done in no-auto mode + ****************************************************************************************/ + +//node is currently in some manager, call it org_manager +//create a copy of node in the dest_manager, which may be different than org_manager +//assumes that org_manager->vtree and dest_manager->vtree have the same structure +//note: if org_manager=dest_manager, then node will be returned +SddNode* sdd_copy(SddNode* node, SddManager* dest_manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_copy"); + assert(!GC_NODE(node)); + + //trivial nodes are special + if(node->type==FALSE) return dest_manager->false_sdd; + if(node->type==TRUE) return dest_manager->true_sdd; + + //node is not trivial + + //find how many nodes + SddSize size = sdd_all_node_count_leave_bits_1(node); + //all nodes are marked 1 now + + //create array to hold copies of sdd nodes + SddNode** node_copies; + CALLOC(node_copies,SddNode*,size,"sdd_copy"); + + //find root of original vtree + Vtree* org_root = node->vtree; + while(org_root->parent!=NULL) org_root = org_root->parent; + + //copy nodes + SddNode* node_copy; + WITH_no_auto_mode(dest_manager,{ + sdd_copy_aux(node,node_copies,&node_copies,org_root,dest_manager->vtree,dest_manager); + //all nodes are maked 0 now + node_copies -= size; + //node_copies is again pointing to first cell in allocated array + node_copy = node_copies[node->index]; + }); + + free(node_copies); + + return node_copy; +} + + +//org_vtree and dest_vtree are isomorphic +//node_vtree appears in org_vtree +//returns the node in dest_vtree which corresponds to node_vtree +Vtree* find_vtree_copy(Vtree* node_vtree, Vtree* org_vtree, Vtree* dest_vtree) { + + if(node_vtree==org_vtree) return dest_vtree; + else if(sdd_vtree_is_sub(node_vtree,org_vtree->left)) + return find_vtree_copy(node_vtree,org_vtree->left,dest_vtree->left); + else //sdd_vtree_is_sub(node_vtree,org_vtree->right) + return find_vtree_copy(node_vtree,org_vtree->right,dest_vtree->right); + +} + + +//compute node copies and store them in associated array +//org_vtree and dest_vtree are isomorphic +//node->vtree is a subvtree of org_vtree +void sdd_copy_aux(SddNode* node, SddNode** start, SddNode*** node_copies_loc, Vtree* org_vtree, Vtree* dest_vtree, SddManager* dest_manager) { + + if(node->bit==0) return; //node has been visited before (i.e., already copied) + else node->bit=0; //this is the first visit to this node + + SddNode* node_copy; + + if(node->type==FALSE) node_copy = dest_manager->false_sdd; + else if(node->type==TRUE) node_copy = dest_manager->true_sdd; + else if(node->type==LITERAL) node_copy = sdd_manager_literal(LITERAL_OF(node),dest_manager); + else { //decomposition + Vtree* vtree_copy = find_vtree_copy(node->vtree,org_vtree,dest_vtree); + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls for copying descendants + sdd_copy_aux(prime,start,node_copies_loc,node->vtree,vtree_copy,dest_manager); + sdd_copy_aux(sub,start,node_copies_loc,node->vtree,vtree_copy,dest_manager); + }); + //copy node + GET_node_from_partition(node_copy,vtree_copy,dest_manager,{ + FOR_each_prime_sub_of_node(prime,sub,node,{ + SddNode* prime_copy = start[prime->index]; + SddNode* sub_copy = start[sub->index]; + DECLARE_element(prime_copy,sub_copy,vtree_copy,dest_manager); + }); + }); + } + + //saving node copy + **node_copies_loc = node_copy; + + //location of saved node copy + node->index = *node_copies_loc-start; + + //advance to next cell in array + (*node_copies_loc)++; + +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/count.c b/pysdd/lib/libsdd-2.0/src/sdds/count.c new file mode 100644 index 0000000..c3b0fa5 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/count.c @@ -0,0 +1,78 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + + +//local declarations +SddSize sdd_node_count_leave_bits_1(SddNode* node); + +/**************************************************************************************** + * count of all nodes in an sdd + ****************************************************************************************/ + +//count the number of ALL nodes (decomposition and terminal) in an sdd +SddSize sdd_all_node_count(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_all_node_count"); + assert(!GC_NODE(node)); + + SddSize count = sdd_all_node_count_leave_bits_1(node); + sdd_clear_node_bits(node); + return count; +} + +//count the number of all nodes in an sdd but leaves bits set to 1 +//this is more efficient than sdd_node_count which has to clear bits +SddSize sdd_all_node_count_leave_bits_1(SddNode* node) { + if (node->bit) return 0; + node->bit = 1; + + SddSize count = 1; + if(node->type==DECOMPOSITION) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + count += sdd_all_node_count_leave_bits_1(prime); + count += sdd_all_node_count_leave_bits_1(sub); + }); + } + return count; +} + + +/**************************************************************************************** + * count of decomposition nodes in an sdd + ****************************************************************************************/ + +//count the number of DECOMPOSITION nodes in an sdd +SddSize sdd_count(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_count"); + assert(!GC_NODE(node)); + + SddSize count = sdd_node_count_leave_bits_1(node); + sdd_clear_node_bits(node); + return count; +} + +//count the number of decomposition nodes in an sdd but leaves bits set to 1 +//this is more efficient than sdd_node_count which has to clear bits +SddSize sdd_node_count_leave_bits_1(SddNode* node) { + if (node->bit) return 0; + node->bit = 1; + + SddSize count = 0; + if(node->type==DECOMPOSITION) { + ++count; + FOR_each_prime_sub_of_node(prime,sub,node,{ + count += sdd_node_count_leave_bits_1(prime); + count += sdd_node_count_leave_bits_1(sub); + }); + } + return count; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c b/pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c new file mode 100644 index 0000000..0129873 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/essential_vars.c @@ -0,0 +1,128 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * identify variables that an sdd essentially depends on + ****************************************************************************************/ + +static void sdd_variables_aux(SddNode* node, int* dependence_map); + +//returns a is_sdd_var, which is an array with the following properties: +//size : 1+number of variables in manager +//is_sdd_var[var]: 1 if the sdd depends on var, 0 otherwise +//is_sdd_var[0] : not used + +int* sdd_variables(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_variables"); + assert(!GC_NODE(node)); + + //allocate dependence map + int* is_sdd_var; + CALLOC(is_sdd_var,int,1+manager->var_count,"sdd_variables"); + //all cells initialized to 0 + + sdd_variables_aux(node,is_sdd_var); + //all nodes are maked 1 now + + sdd_clear_node_bits(node); + //all nodes are marked 0 now + + return is_sdd_var; +} + +//set dependence_map[var] to 1 if the node depends on var +void sdd_variables_aux(SddNode* node, int* is_sdd_var) { + + if(node->bit==1) return; //node has been visited before + else node->bit=1; //this is the first visit to this node + + if(IS_LITERAL(node)) { + SddLiteral var = VAR_OF(node); + is_sdd_var[var] = 1; //node essentially depends on this variable + } + else if(IS_DECOMPOSITION(node)) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls on descendants + sdd_variables_aux(prime,is_sdd_var); + sdd_variables_aux(sub,is_sdd_var); + }); + } + +} + + +/**************************************************************************************** + * v->all_vars_in_sdd: all vars of vtree v appear in sdd + * v->no_var_in_sdd : no var of vtree v appears in sdd + * + * sets the above flags for each vtree node v in node->vtree + ****************************************************************************************/ + +static void set_sdd_variables_aux(SddNode* node); +static void initialize_sdd_variables(Vtree* vtree); +static void propagate_sdd_variables(Vtree* vtree); + +void set_sdd_variables(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"set_no_var_in_sdd"); + assert(!GC_NODE(node)); + + initialize_sdd_variables(manager->vtree); + + //these nodes have no vtree + if(IS_FALSE(node) || IS_TRUE(node)) return; + + set_sdd_variables_aux(node); + //all nodes are maked 1 now + + sdd_clear_node_bits(node); + //all nodes are marked 0 now + + propagate_sdd_variables(node->vtree); +} + +static +void set_sdd_variables_aux(SddNode* node) { + + if(node->bit==1) return; //node has been visited before + else node->bit=1; //this is the first visit to this node + + if(IS_LITERAL(node)) { + node->vtree->all_vars_in_sdd = 1; + node->vtree->no_var_in_sdd = 0; + } + else if(IS_DECOMPOSITION(node)) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls on descendants + set_sdd_variables_aux(prime); + set_sdd_variables_aux(sub); + }); + } + +} + +static +void initialize_sdd_variables(Vtree* vtree) { + FOR_each_vtree_node(v,vtree,{ + v->all_vars_in_sdd = 0; + v->no_var_in_sdd = 1; + }); +} + +static +void propagate_sdd_variables(Vtree* vtree) { + if(INTERNAL(vtree)) { + propagate_sdd_variables(vtree->left); + propagate_sdd_variables(vtree->right); + vtree->all_vars_in_sdd = vtree->left->all_vars_in_sdd && vtree->right->all_vars_in_sdd; + vtree->no_var_in_sdd = vtree->left->no_var_in_sdd && vtree->right->no_var_in_sdd; + } +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/exists.c b/pysdd/lib/libsdd-2.0/src/sdds/exists.c new file mode 100644 index 0000000..00d71c8 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/exists.c @@ -0,0 +1,29 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + + +/**************************************************************************************** + * existentially quantifying an sdd + ****************************************************************************************/ + +//existentially quantify a variable out of an sdd +SddNode* sdd_exists(SddLiteral var, SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_exists"); + + //condition will not do auto gc/minimize + SddNode* p_cond = sdd_condition(var,node,manager); + SddNode* n_cond = sdd_condition(-var,node,manager); + + //apply will not gc its arguments + return sdd_apply(p_cond,n_cond,DISJOIN,manager); +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c b/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c new file mode 100644 index 0000000..760107b --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple.c @@ -0,0 +1,311 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +//basic/shadows.c +SddNode* shadow_node(NodeShadow* shadow); +ElmShadow* shadow_elements(NodeShadow* shadow); +int shadow_is_terminal(NodeShadow* shadow); +SddShadows* shadows_new(SddSize root_count, SddNode** root_nodes, SddManager* manager); +void shadows_traverse(void (*fn)(NodeShadow*,SddShadows*), SddShadows* shadows); +void shadows_free(SddShadows* shadows); + +//sdds/apply.c +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited); + +//local declarations +static void initialize_for_shadows(SddNode* node, int* exists_map, SddManager* manager); +static SddNode* quantify_shadow(NodeShadow* shadow, int* exists_map, SddManager* manager); +static void ref_nodes_of_terminal_shadows(SddShadows* shadows); +static void deref_nodes_of_terminal_shadows(SddShadows* shadows); + +/**************************************************************************************** + * existentially quantify a number of variables from an sdd + ****************************************************************************************/ + +//exists_map is an array with the following properties: +//size : 1+number of variables in manager +//exists_map[var] : is 1 if var is to be existentially quantified, 0 otherwise +//exists_map[0] : not used + +static long long ref_count; //sanity check + +SddNode* sdd_exists_multiple(int* exists_map, SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_exists_multiple"); + assert(!GC_NODE(node)); + + if(node->type==FALSE || node->type==TRUE) return node; + + ref_count = 0; + + SddNode** roots = (SddNode**) malloc(sizeof(SddNode*)); + roots[0] = node; + initialize_for_shadows(node,exists_map,manager); //before constructing shadows + + SddShadows* shadows = shadows_new(1,roots,manager); + NodeShadow* shadow = shadows->root_shadows[0]; + assert(shadow); + assert(shadow->ref_count==1); + + ref_nodes_of_terminal_shadows(shadows); + SddNode* q_node; + + q_node = quantify_shadow(shadow,exists_map,manager); + + deref_nodes_of_terminal_shadows(shadows); + + shadows_free(shadows); + free(roots); + + assert(ref_count==0); + + return q_node; +} + +/**************************************************************************************** + * set node->shadow_type to + * --'t', if node is a terminal sdd or does not contain quantified variables + * --'g' otherwise + * + * set node->shadow to NULL for all nodes + * + * set node->map to + * --NULL, if node contains quantified variables + * --node, if node does not contain quantified variables + * + * node->map is only used temporarily in the initialization function + ****************************************************************************************/ + +//this function will leave all bits set to 1 --- bits must be cleared (see bits.c) +static +void initialize(SddNode* node, int* exists_map, SddManager* manager) { + if(node->bit) return; //already visited + else node->bit = 1; //first visit + + node->shadow_type = 'g'; //default: may be changed later + node->shadow = NULL; //all nodes + node->map = NULL; //default: may change later + + if(node->type==TRUE || node->type==FALSE) { //node will not be quantified + node->shadow_type = 't'; + node->map = node; + } + else if(node->type==LITERAL) { //node will not be quantified + node->shadow_type = 't'; + SddLiteral var = VAR_OF(node); + if(exists_map[var]==0) node->map = node; //node will not be quantified + } + else { + int q_vars = 0; + FOR_each_prime_sub_of_node(prime,sub,node,{ + initialize(prime,exists_map,manager); + initialize(sub,exists_map,manager); + q_vars = q_vars || prime->map==NULL || sub->map==NULL; + }); + if(q_vars==0) { //node will not be quantified + node->shadow_type = 't'; + node->map = node; + } + } +} + +static +void initialize_for_shadows(SddNode* node, int* exists_map, SddManager* manager) { + initialize(node,exists_map,manager); //leave bits 1 + sdd_clear_node_bits(node); +} + +/**************************************************************************************** + * ref/deref the decomposition nodes of terminal shadows + ****************************************************************************************/ + +static +void ref_terminal(NodeShadow* shadow, SddShadows* shadows) { + if(shadow_is_terminal(shadow)) { + SddNode* node = shadow_node(shadow); + assert(node); + if(node->type==DECOMPOSITION) sdd_ref(node,shadows->manager); + } +} + +static +void deref_terminal(NodeShadow* shadow, SddShadows* shadows) { + if(shadow_is_terminal(shadow)) { + SddNode* node = shadow_node(shadow); + assert(node); + if(node->type==DECOMPOSITION) { + assert(LIVE(node)); + sdd_deref(node,shadows->manager); + } + } +} + +static +void ref_nodes_of_terminal_shadows(SddShadows* shadows) { + shadows_traverse(ref_terminal,shadows); +} + +static +void deref_nodes_of_terminal_shadows(SddShadows* shadows) { + shadows_traverse(deref_terminal,shadows); +} + +/**************************************************************************************** + * lca + ****************************************************************************************/ + +//returns NULL if elements do not form an xy-decomposition +//returns lca of elements otherwise +//assumes primes form a partition (with no trivial primes) +//assumes at least one non-trivial sub +static +Vtree* lca(SddNode* non_trivial_sub, SddNodeSize size, ElmShadow* elements, SddManager* manager) { + Vtree* pvt = elements[0].prime->cache->vtree; + Vtree* svt = non_trivial_sub->vtree; + assert(pvt && svt); + if(pvt->position >= svt->position) return NULL; + Vtree* lca = sdd_vtree_lca(pvt,svt,manager->vtree); + for(ElmShadow* e=elements; eprime->cache; + assert(prime); + assert(prime->id== e->prime->cache_id); + SddNode* sub = e->sub->cache; + assert(sub); + assert(sub->id== e->sub->cache_id); + assert(prime->vtree); + if(!sdd_vtree_is_sub(prime->vtree,lca->left)) return NULL; + if(sub->vtree && !sdd_vtree_is_sub(sub->vtree,lca->right)) return NULL; + assert(sub->vtree==NULL || lca==sdd_vtree_lca(prime->vtree,sub->vtree,manager->vtree)); + } + return lca; +} + +/**************************************************************************************** + * quantifying nodes + * + * nodes of terminal shadows are protected (referenced) and released by the shadows + * utility; otherwise, they may be gc'd as they lose their parents during vtree + * minimization (even if the root sdd node is referenced before quantifying it) + ****************************************************************************************/ + +//fix is_partition case + +static +void deref_elements(SddNodeSize size, ElmShadow* elements, SddManager* manager) { + for(ElmShadow* e=elements; eprime->cache,manager); + sdd_deref(e->sub->cache,manager); + } +} + +//checks whether the node of shadow needs quantification +static inline +int no_quantified_vars(NodeShadow* shadow) { + assert(!shadow_is_terminal(shadow) || LIVE(shadow_node(shadow))); + return shadow_is_terminal(shadow) && shadow_node(shadow)==shadow->cache; +} + +static +SddNode* quantify_shadow(NodeShadow* shadow, int* exists_map, SddManager* manager) { + assert(shadow->cache==NULL || shadow->cache->id==shadow->cache_id); + if(shadow->cache && shadow->cache->id==shadow->cache_id) { + //cached node exists and has not been gc'd + assert(ref_count>0); + --ref_count; + sdd_deref(shadow->cache,manager); + return shadow->cache; + } + + SddNode* q_node; + + if(shadow_is_terminal(shadow)) { + q_node = shadow_node(shadow); + assert(q_node); + assert(LIVE(q_node)); + if(q_node->type==LITERAL) { + SddLiteral var = VAR_OF(q_node); + if(exists_map[var]) q_node = manager->true_sdd; + } + } + else { + SddNodeSize size = shadow->size; + ElmShadow* elements = shadow_elements(shadow); + + //quantify shadow elements and determine type of decomposition + int is_true_element = 0; + int is_true_subs = 1; + int is_same_primes = 1; + SddNode* non_trivial_sub = NULL; + for(ElmShadow* e=elements; eprime,exists_map,manager); + assert(prime==e->prime->cache); + sdd_ref(prime,manager); + SddNode* sub = quantify_shadow(e->sub,exists_map,manager); + assert(prime==e->prime->cache); + sdd_ref(sub,manager); + assert(LIVE(prime) && LIVE(sub)); + is_true_element |= IS_TRUE(prime) && IS_TRUE(sub); + is_true_subs &= IS_TRUE(sub); + is_same_primes &= no_quantified_vars(e->prime); + if(non_trivial_sub==NULL && !TRIVIAL(sub)) non_trivial_sub = sub; + } + + Vtree* vtree = NULL; + + if(is_true_element || is_true_subs) { + deref_elements(size,elements,manager); + q_node = manager->true_sdd; + } + else if(non_trivial_sub==NULL && size==2) { //must come after is_true_subs + deref_elements(size,elements,manager); + SddNode* sub0 = elements[0].sub->cache; + q_node = IS_TRUE(sub0)? elements[0].prime->cache: elements[1].prime->cache; + } + + else if(is_same_primes && non_trivial_sub && (vtree=lca(non_trivial_sub,size,elements,manager))) { + //partition but may not be compressed + deref_elements(size,elements,manager); + GET_node_from_partition(q_node,vtree,manager, + for(ElmShadow* e=elements; eprime->cache; + SddNode* q_sub = e->sub->cache; + assert(q_prime->vtree && sdd_vtree_is_sub(q_prime->vtree,vtree->left)); + assert(q_sub->vtree==NULL || sdd_vtree_is_sub(q_sub->vtree,vtree->right)); + DECLARE_element(q_prime,q_sub,vtree,manager); + } + ); + } + + else { //must construct quantified node through apply + q_node = manager->false_sdd; + for(ElmShadow* e=elements; eprime->cache; + SddNode* yq_node = e->sub->cache; + assert(LIVE(xq_node) && LIVE(yq_node)); + sdd_deref(xq_node,manager); //release + sdd_deref(yq_node,manager); //release + sdd_ref(q_node,manager); //protect + SddNode* q_element = apply(xq_node,yq_node,CONJOIN,manager,0); + sdd_deref(q_node,manager); //release + q_node = apply(q_element,q_node,DISJOIN,manager,0); + } + } + } + + //cached nodes are not protected so they may be gc'd + //the cache_id field will be used to decide whether thet cached node was gc'd + shadow->cache_id = q_node->id; + assert(ref_count>=0); + for(SddRefCount i=0; iref_count-1; i++) { ++ref_count; sdd_ref(q_node,manager); } + + return shadow->cache = q_node; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c b/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c new file mode 100644 index 0000000..d85a863 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/exists_multiple_static.c @@ -0,0 +1,165 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static void initialize_sdd(SddNode* node, int* exists_map, SddManager* manager); +static void quantify_sdd(SddNode* node, SddManager* manager); + +/**************************************************************************************** + * existentially quantify a number of variables from an sdd + * + * this version will NOT invoke auto gc and minimize + ****************************************************************************************/ + +//exists_map is an array with the following properties: +//size : 1+number of variables in manager +//exists_map[var] : is 1 if var is to be existentially quantified, 0 otherwise +//exists_map[0] : not used + +SddNode* sdd_exists_multiple_static(int* exists_map, SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_exists_multiple_static"); + assert(!GC_NODE(node)); + + if(node->type==FALSE || node->type==TRUE) return node; + + initialize_sdd(node,exists_map,manager); + + WITH_no_auto_mode(manager,{ //no gc or minimize + quantify_sdd(node,manager); + }); + + return node->map; +} + +/**************************************************************************************** + * set node->map set to NULL if node contains quantified variables, set to node otherwise + ****************************************************************************************/ + +//this function will leave all bits set to 1 --- bits must be cleared (see bits.c) +static +void initialize(SddNode* node, int* exists_map, SddManager* manager) { + if(node->bit) return; //already visited + else node->bit = 1; //first visit + + node->map = NULL; //default + + if(node->type==TRUE || node->type==FALSE) node->map = node; + else if(node->type==LITERAL) { + SddLiteral var = VAR_OF(node); + if(exists_map[var]==0) node->map = node; + } + else { + int q_vars = 0; + FOR_each_prime_sub_of_node(prime,sub,node,{ + initialize(prime,exists_map,manager); + initialize(sub,exists_map,manager); + q_vars = q_vars || prime->map==NULL || sub->map==NULL; + }); + if(q_vars==0) node->map = node; + } + +} + +static void initialize_sdd(SddNode* node, int* exists_map, SddManager* manager) { + initialize(node,exists_map,manager); //leave bits 1 + sdd_clear_node_bits(node); +} + +/**************************************************************************************** + * quantifying nodes + ****************************************************************************************/ + +//elements form an xy-partition (may not be compressed) +static +SddNode* quantify_from_partition(SddNodeSize size, SddElement* elements, Vtree* vtree, SddManager* manager) { + SddNode* q_node; + + GET_node_from_partition(q_node,vtree,manager,{ + for(SddElement* e=elements; eprime,e->sub,vtree,manager); + } + }); + + return q_node; +} + +//elements form an xy-decomposition +static +SddNode* quantify_from_decomposition(SddNodeSize size, SddElement* elements, SddManager* manager) { + + SddNode* q_node = manager->false_sdd; + + for(SddElement* e=elements; eprime; + SddNode* y_node = e->sub; + SddNode* element = sdd_apply(x_node,y_node,CONJOIN,manager); + q_node = sdd_apply(element,q_node,DISJOIN,manager); + } + + return q_node; +} + +//returned elements are not a compressed parition, but just x-nodes and y-nodes for some x and y +//reasons: quantification weakens nodes, potentially destroying disjointness (primes) or distinction (subs) +static +SddElement* get_quantified_elements(SddNode* node) { + assert(node->type==DECOMPOSITION); + + SddNodeSize size = node->size; + SddElement* elements = ELEMENTS_OF(node); + + SddElement* q_elements; + CALLOC(q_elements,SddElement,size,"get_quantified_elements"); + + for(SddNodeSize i=0; imap; + //quantified subs may not be distinct + q_elements[i].sub = elements[i].sub->map; + } + + return q_elements; +} + +static +void quantify_sdd(SddNode* node, SddManager* manager) { + if(node->map) return; //node already quantified + assert(!TRIVIAL(node)); + + if(node->type==LITERAL) node->map = manager->true_sdd; + else { + + int is_true = 0; + int is_partition = 1; + FOR_each_prime_sub_of_node(prime,sub,node,{ + quantify_sdd(prime,manager); //prime->map is set now + quantify_sdd(sub,manager); //sub->map is set now + is_true = is_true || (IS_TRUE(prime->map) && IS_TRUE(sub->map)); + is_partition = is_partition && prime==prime->map; + }); + + if(is_true) { //optimization + node->map = manager->true_sdd; + return; + } + + Vtree* vtree = node->vtree; + SddSize size = node->size; + SddElement* q_elements = get_quantified_elements(node); + + if(is_partition) node->map = quantify_from_partition(size,q_elements,vtree,manager); + else node->map = quantify_from_decomposition(size,q_elements,manager); + + free(q_elements); + } + +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/forall.c b/pysdd/lib/libsdd-2.0/src/sdds/forall.c new file mode 100644 index 0000000..2fcd7af --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/forall.c @@ -0,0 +1,28 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + + +/**************************************************************************************** + * universally quantify a variable out of an sdd + ****************************************************************************************/ + +//universally quantify a variable out of an sdd +SddNode* sdd_forall(SddLiteral var, SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_forall"); + + //condition will not do auto gc/minimize + SddNode* p_cond = sdd_condition(var,node,manager); + SddNode* n_cond = sdd_condition(-var,node,manager); + + //apply will not gc its arguments + return sdd_apply(p_cond,n_cond,CONJOIN,manager); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/io.c b/pysdd/lib/libsdd-2.0/src/sdds/io.c new file mode 100644 index 0000000..e16abde --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/io.c @@ -0,0 +1,448 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static SddNode* parse_sdd_file(char* buffer, SddManager* manager); + +/**************************************************************************************** + * printing sdd to .dot file + ****************************************************************************************/ + +//terminal nodes are smaller than decomposition nodes +//decomposition node n1 is smaller than decomposition node n2 iff n1->vtree->position < n2->vtree->position +int sdd_node_comparator(const void* xp, const void* yp) { + + SddNode* x = *((SddNode**) xp); + SddNode* y = *((SddNode**) yp); + + //vtree positions start at 0 + SddSize xx = x->type==DECOMPOSITION? x->vtree->position: -1; + SddSize yy = y->type==DECOMPOSITION? y->vtree->position: -1; + + if (xx > yy) return -1; + else if (xx < yy) return 1; + else return 0; + +} + + +char* get_sdd_node_label(SddNode* node) { + + char* true_label = (char*) "⊤"; //watch this cast + char* false_label = (char*) "⊥"; //watch this cast + char* no_label = (char*) ""; //watch this cast + + if(node->type==TRUE) return true_label; + else if(node->type==FALSE) return false_label; + else if(node->type==LITERAL) return literal_to_label(LITERAL_OF(node)); + else return no_label; + +} + + +void print_terminal_sdd_node_as_dot(FILE* file, SddNode* node) { + + char* label = get_sdd_node_label(node); + fprintf(file,"\nn%"PRIsS" [label= \"%s\",shape=box]; ",node->id,label); + if (node->type==LITERAL) free(label); + return; + +} + +void print_decomposition_sdd_node_as_dot(FILE* file, SddNode* node) { + + static const char* node_format = "\nn%"PRIsS" [label= \"%"PRIsS"\",style=filled,fillcolor=gray95,shape=circle,height=.25,width=.25]; "; + static const char* element_format = "\nn%"PRIsS"e%"PRIsS"\n" + " [label= \"%s|%s\",\n" + " shape=record,\n" + " fontsize=20,\n" + " fontname=\"Times-Italic\",\n" + " fillcolor=white,\n" + " style=filled,\n" + " fixedsize=true,\n" + " height=.30, \n" + " width=.65];\n"; + + static const char* or_format = "\nn%"PRIsS"->n%"PRIsS"e%"PRIsS" [arrowsize=.50];"; + static const char* prime_format = "\nn%"PRIsS"e%"PRIsS":L:c->n%"PRIsS"" + " [arrowsize=.50,tailclip=false,arrowtail=dot,dir=both];"; + static const char* sub_format = "\nn%"PRIsS"e%"PRIsS":R:c->n%"PRIsS"" + " [arrowsize=.50,tailclip=false,arrowtail=dot,dir=both];"; + + //decision node + fprintf(file,node_format,node->id,node->vtree->position); + + SddSize i=0; + FOR_each_prime_sub_of_node(prime,sub,node,{ + + char* prime_label = get_sdd_node_label(prime); + char* sub_label = get_sdd_node_label(sub); + //element: prime & sub + fprintf(file,element_format,node->id,i,prime_label,sub_label); + + if(prime->type == LITERAL) free(prime_label); + if(sub->type == LITERAL) free(sub_label); + //edge into element + fprintf(file,or_format,node->id,node->id,i); + //edge out of prime cell + if(prime->type==DECOMPOSITION) fprintf(file,prime_format,node->id,i,prime->id); + //edge out of sub cell + if(sub->type==DECOMPOSITION) fprintf(file,sub_format,node->id,i,sub->id); + ++i; + }); +} + +//two nodes have the same rank iff they are normalized for the same vtree (better visualization) +void print_sdd_node_ranks(FILE* file, SddSize count, SddNode** nodes) { + assert(count>0); + + while(count) { + assert((*nodes)->type==DECOMPOSITION); + Vtree* vtree = (*nodes)->vtree; //vtree of next group of nodes (same rank) + fprintf(file,"\n{rank=same; "); + while(count && (*nodes)->vtree==vtree) { + fprintf(file,"n%"PRIsS" ",(*nodes)->id); + --count; + ++nodes; + } + fprintf(file,"}"); + } + fprintf(file,"\n"); + +} + +void print_sdd_nodes_as_dot(FILE* file, SddSize count, SddNode** nodes) { + assert(count>1); + + //sort nodes so that: + //--terminal nodes appear first, followed by decomposition nodes + //--decomposition nodes are sorted by their vtrees + qsort(nodes,count,sizeof(SddNode*),sdd_node_comparator); + + //skip terminal nodes + while((*nodes)->type!=DECOMPOSITION) { --count; ++nodes; } + + assert(count!=0); //at least one decomposition node + + //declare the ranks of decomposition nodes (equal rank iff normalized for same vtree) + print_sdd_node_ranks(file,count,nodes); + + //print decomposition nodes (terminal nodes will be printed as a side effect) + for(SddSize i=0; ibit==0) return nodes-1; + node->bit=0; + + *nodes = node; + SddNode** end = nodes; + + if(node->type==DECOMPOSITION) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + end=collect_all_nodes(prime,1+end); + end=collect_all_nodes(sub,1+end); + }); + } + + return end; +} + + +//prints an SDD in .dot file format +void print_sdd_as_dot(FILE* file, SddNode* node) { + + fprintf(file,"\ndigraph sdd {"); + fprintf(file,"\n\noverlap=false"); + fprintf(file,"\n"); + + if(node->type!=DECOMPOSITION) { + //a single terminal node + print_terminal_sdd_node_as_dot(file,node); + } + else { + + //count sdd nodes + SddSize count = sdd_all_node_count_leave_bits_1(node); + //all nodes are now marked 1 + + //put them in an array + SddNode** nodes; + CALLOC(nodes,SddNode*,count,"print_sdd_as_dot"); + collect_all_nodes(node,nodes); + //all nodes are now marked 0 + + //print nodes + print_sdd_nodes_as_dot(file,count,nodes); + + free(nodes); + } + + fprintf(file,"\n\n"); + fprintf(file,"\n}"); +} + +void sdd_save_as_dot(const char* fname, SddNode *node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_save_as_dot"); + + FILE *file = fopen(fname,"w"); + print_sdd_as_dot(file,node); + fclose(file); +} + + +/**************************************************************************************** + * printing sdd to .dot file: all nodes normalized for a vtree node + ****************************************************************************************/ + +//prints an SDD in .dot file format +void print_sdds_as_dot(FILE* file, Vtree* vtree) { + + fprintf(file,"\ndigraph sdd {"); + fprintf(file,"\n\noverlap=false"); + fprintf(file,"\n"); + + if(LEAF(vtree)) { + print_terminal_sdd_node_as_dot(file,vtree->nodes); //positive literal + print_terminal_sdd_node_as_dot(file,vtree->nodes->vtree_next); //negative literal + } + else { + + //count nodes + SddSize count = 0; + FOR_each_sdd_node_normalized_for(node,vtree,count += sdd_all_node_count_leave_bits_1(node)); + //all nodes are now marked 1 + + //put nodes into an array + SddNode** nodes; + CALLOC(nodes,SddNode*,count,"print_sdds_as_dot"); + SddNode** end = nodes-1; + FOR_each_sdd_node_normalized_for(node,vtree,end = collect_all_nodes(node,end+1)); + //all nodes are now marked 0 + + //print nodes + print_sdd_nodes_as_dot(file,count,nodes); + + free(nodes); + } + + fprintf(file,"\n\n"); + fprintf(file,"\n}"); +} + +//saving a multi-rooted sdd (roots are all nodes normalized for vtree) +void save_shared_sdd_as_dot_vt(const char* fname, Vtree* vtree) { + FILE *file = fopen(fname,"w"); + print_sdds_as_dot(file,vtree); + fclose(file); +} + +//saves the multi-rooted sdd of manager +void sdd_shared_save_as_dot(const char* fname, SddManager* manager) { + save_shared_sdd_as_dot_vt(fname,manager->vtree); +} + + +/**************************************************************************************** + * printing sdd to .sdd file + * + * saved nodes are numbered continguously starting from 0 + ****************************************************************************************/ + +//used to contiguously id nodes and elements before saving +SddSize node_id_counter; + +void print_sdd_header(FILE* file, SddSize count) { + static const char* header = + "c ids of sdd nodes start at 0\n" + "c sdd nodes appear bottom-up, children before parents\n" + "c\n" + "c file syntax:\n" + "c sdd count-of-sdd-nodes\n" + "c F id-of-false-sdd-node\n" + "c T id-of-true-sdd-node\n" + "c L id-of-literal-sdd-node id-of-vtree literal\n" + "c D id-of-decomposition-sdd-node id-of-vtree number-of-elements {id-of-prime id-of-sub}*\n" + "c\n"; + fprintf(file,"%s",header); + fprintf(file,"sdd %"PRIsS"\n",count); +} + +//index stores new (continguous) ids +void print_sdd_node_file(FILE* file, SddNode* node) { + + Vtree* vtree = node->vtree; //NULL for trivial nodes + + if(node->type==TRUE) fprintf(file,"T %"PRIsS"\n",node->index); + else if(node->type==FALSE) fprintf(file,"F %"PRIsS"\n",node->index); + else if(node->type==LITERAL) fprintf(file,"L %"PRIsS" %"PRIsS" %"PRIlitS"\n",node->index,vtree->position,LITERAL_OF(node)); + else {//decomposition + fprintf(file,"D %"PRIsS" %"PRIsS" %"PRIsS"",node->index,vtree->position,node->size); + FOR_each_prime_sub_of_node(prime,sub,node,fprintf(file," %"PRIsS" %"PRIsS"",prime->index,sub->index)); + fprintf(file,"\n"); + } +} + +void print_sdd_recurse(FILE* file, SddNode* node) { + if (node->bit==0) return; //node already visited (i.e., already printed) + node->bit=0; + + node->index = node_id_counter++; //new id + if(node->type==DECOMPOSITION) { + FOR_each_prime_sub_of_node(prime,sub,node,{ + print_sdd_recurse(file,prime); + print_sdd_recurse(file,sub); + }); + } + print_sdd_node_file(file,node); +} + +void print_sdd(FILE* file, SddNode* node) { + SddSize count = sdd_all_node_count_leave_bits_1(node); + //all node bits are now set to 1 + print_sdd_header(file,count); + node_id_counter=0; + print_sdd_recurse(file,node); + //all node bits are now set to 0 +} + +void sdd_save(const char* fname, SddNode *node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"save_sdd"); + assert(!GC_NODE(node)); + + FILE *file = fopen(fname,"w"); + print_sdd(file,node); + fclose(file); +} + + +/**************************************************************************************** + * reading sdd + ****************************************************************************************/ + + +//reads an SDD from a .sdd file +SddNode* sdd_read(const char* filename, SddManager* manager) { + char* buffer = read_file(filename); + char* filtered = filter_comments(buffer); + SddNode* node; + + //auto gc and minimize will not be invoked during reading + WITH_no_auto_mode(manager,{ + node = parse_sdd_file(filtered,manager); + }); + + free(buffer); + free(filtered); + return node; +} + + +/***************************************************************************************** + * ids of sdd nodes start at 0 + * ids of node are continguous + * sdd nodes appear bottom-up, children before parents + * + * file syntax: + * sdd count-of-sdd-nodes + * F id-of-false-sdd-node + * T id-of-true-sdd-node + * L id-of-literal-sdd-node id-of-vtree literal + * D id-of-decomposition-sdd-node id-of-vtree number-of-elements {id-of-prime id-of-sub}* +*****************************************************************************************/ + +SddNode* parse_sdd_file(char* buffer, SddManager* manager) { + Vtree** pos2vnode_map(Vtree* vtree); + + Vtree* vtree = manager->vtree; + Vtree** vtree_list = pos2vnode_map(vtree); //maps ids to vnodes + + // 1st token is "sdd" + header_strtok(buffer,"sdd"); + + // read count of nodes + SddSize node_count = int_strtok(); + + //create map from ids to nodes + SddNode** node_list; + CALLOC(node_list,SddNode*,node_count,"parse_sdd_file"); + + //create buffers for primes/subs + SddNodeSize max_size = 16; + SddNode** prime_list; + CALLOC(prime_list,SddNode*,max_size,"parse_sdd_file"); + SddNode** sub_list; + CALLOC(sub_list,SddNode*,max_size,"parse_sdd_file"); + + SddNode* root = NULL; + while (node_count--) { + char node_type = char_strtok(); + SddSize sdd_node_id = int_strtok(); + + if(node_type=='T') node_list[sdd_node_id]=manager->true_sdd; + else if(node_type=='F') node_list[sdd_node_id]=manager->false_sdd; + else if(node_type=='L') { + int_strtok(); //id of vtree: not used + SddLiteral lit = int_strtok(); + node_list[sdd_node_id] = sdd_manager_literal(lit,manager); + } + else { //(node_type == 'D') + Vtree* vnode = vtree_list[int_strtok()]; + SddNodeSize size = int_strtok(); + + if(size > max_size) { //make sure prime/sub buffers are large enough + max_size = size; + REALLOC(prime_list,SddNode*,max_size,"parse_sdd_file"); + REALLOC(sub_list,SddNode*,max_size,"parse_sdd_file"); + } + + //read/store elements and check if structured + int structured_elements = 1; + for(SddNodeSize i=0; ivtree,vnode->left); + structured_elements &= TRIVIAL(sub) || sdd_vtree_is_sub(sub->vtree,vnode->right); + } + + SddNode* node; + if(structured_elements) { + GET_node_from_partition(node,vnode,manager,{ + for(SddNodeSize i=0; ifalse_sdd; + for(SddNodeSize i=0; ivtree) + +//count the models of an sdd +SddModelCount sdd_model_count(SddNode* node, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_model_count"); + assert(!GC_NODE(node)); + + if(node->type==FALSE) return 0; + if(node->type==TRUE) return 1; + + //mark vtree nodes depending on whether their variables appear in the sdd + set_sdd_variables(node,manager); + + //find how many nodes + SddSize size = sdd_all_node_count_leave_bits_1(node); + //all nodes are marked 1 now + + //create array to hold model counts of sdd nodes + SddModelCount* model_counts; + CALLOC(model_counts,SddModelCount,size,"sdd_model_count"); + + sdd_model_count_aux(node,model_counts,&model_counts); + //all nodes are maked 0 now + + model_counts -= size; + //model_counts is again pointing to first cell in allocated array + + SddModelCount model_count = model_counts[node->index]; + free(model_counts); + + return model_count; +} + + +//computes model counts and stores them in the associated array +static +void sdd_model_count_aux(SddNode* node, SddModelCount* start, SddModelCount** model_counts_loc) { + + if(node->bit==0) return; //node has been visited before + else node->bit=0; //this is the first visit to this node + + SddModelCount mc = 0; + + if(node->type==FALSE) mc = 0; //node must be a sub + else if(node->type==TRUE) ; //node must be a sub, case not used + else if(node->type==LITERAL) mc = 1; + else { //decomposition + FOR_each_prime_sub_of_node(prime,sub,node,{ + //recursive calls + sdd_model_count_aux(prime,start,model_counts_loc); + sdd_model_count_aux(sub,start,model_counts_loc); + + //prime is neither true nor false, sub could be true or false + if(!IS_FALSE(sub)) { + Vtree* left = node->vtree->left; + Vtree* right = node->vtree->right; + + SddModelCount prime_mc = + start[prime->index]*pow(2,gap_var_count(left,prime->vtree)); + SddModelCount sub_mc = (IS_TRUE(sub)? pow(2,var_count(right)): + start[sub->index]*pow(2,gap_var_count(right,sub->vtree))); + + mc += prime_mc*sub_mc; + } + }); + } + + //saving model count + **model_counts_loc = mc; + + //location of saved model count + node->index = *model_counts_loc-start; + + //advance to next cell in array + (*model_counts_loc)++; + +} + +// performs model count relative to all variables, in contrast to used +// variables as in sdd_model_count +SddModelCount sdd_global_model_count(SddNode* node, SddManager* manager) { + SddModelCount mc = sdd_model_count(node,manager); + + // count unused variables + int* vars = sdd_variables(node,manager); + SddLiteral var_count = sdd_manager_var_count(manager); + SddLiteral unused = 0; + for (SddLiteral var = 1; var <= var_count; var++) + if ( vars[var] == 0 ) unused += 1; + free(vars); + + return mc << unused; // multiply by 2^unused +} + +/**************************************************************************************** + * counting vtree variables that appear in the sdd + * + * these are needed to handle + * --true subs: variables of true sub depend on where the sub appears (in which decomposition node) + * --primes not normalized for vtree->left: variables in vtree->right but not in sub->vtree + * --subs not normalized for vtree->right: variables in vtree->right but not in sub->vtree + * + ****************************************************************************************/ + +//returns count of sdd variables in vtree +static +SddLiteral var_count(Vtree* vtree) { + if(vtree->all_vars_in_sdd) return vtree->var_count; + else if(vtree->no_var_in_sdd) return 0; + else return var_count(vtree->left) + var_count(vtree->right); +} + +//returns count of sdd variables that appear in vtree but not in sub_vtree +static +SddLiteral gap_var_count(Vtree* vtree, Vtree* sub_vtree) { + if(vtree==sub_vtree) return 0; + if(sdd_vtree_is_sub(sub_vtree,vtree->left)) { + return gap_var_count(vtree->left,sub_vtree) + var_count(vtree->right); + } + else { + return var_count(vtree->left) + gap_var_count(vtree->right,sub_vtree); + } +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c b/pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c new file mode 100644 index 0000000..0538a2f --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/rename_vars.c @@ -0,0 +1,113 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +void sdd_rename_variables_aux(SddNode* node, SddLiteral* variable_map, SddManager* manager); +static void initialize_map(SddNode* node, SddLiteral* variable_map); + +/**************************************************************************************** + * given an sdd, and a variable map, + * construct a new sdd by mapping the variables of the original sdd into new ones + * + * will not do auto gc/minmize as its computations are done in no-auto mode + ****************************************************************************************/ + +//returns an sdd which is obtained by renaming variables in the original sdd (node) +//variable_map is an array with the following properties: +//size : 1+number of variables in manager +//variable_map[var]: is a variable into which var is mapped +//variable_map[0] : not used + +SddNode* sdd_rename_variables(SddNode* node, SddLiteral* variable_map, SddManager* manager) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_rename_variables"); + assert(!GC_NODE(node)); + + if(node->type==FALSE || node->type==TRUE) return node; + //node is not trivial + + WITH_no_auto_mode(manager,{ + initialize_map(node,variable_map); + sdd_rename_variables_aux(node,variable_map,manager); + }); + + return node->map; +} + +//compute node maps and store them in the map field +void sdd_rename_variables_aux(SddNode* node, SddLiteral* variable_map, SddManager* manager) { + + if(node->map!=NULL) return; + + SddNode* node_map; + + if(node->type==FALSE || node->type==TRUE) node_map = node; + else if(node->type==LITERAL) { //must be renamed + SddLiteral old_literal = LITERAL_OF(node); + SddLiteral old_var = VAR_OF(node); + SddLiteral new_var = variable_map[old_var]; + assert(new_var!=old_var); + assert(1<= old_var && old_var <= sdd_manager_var_count(manager)); + assert(1<= new_var && new_var <= sdd_manager_var_count(manager)); + SddLiteral new_literal = old_literal>0? new_var: -new_var; + node_map = sdd_manager_literal(new_literal,manager); + } + else { //decomposition + node_map = manager->false_sdd; + FOR_each_prime_sub_of_node(prime,sub,node,{ + sdd_rename_variables_aux(prime,variable_map,manager); + sdd_rename_variables_aux(sub,variable_map,manager); + SddNode* element_map = sdd_apply(prime->map,sub->map,CONJOIN,manager); + node_map = sdd_apply(node_map,element_map,DISJOIN,manager); + }); + } + + node->map = node_map; + +} + +/**************************************************************************************** + * initialized the mapped field of nodes: + * + * node->map=node if node does not include a renamed var + * node->map=NULL otherwise + ****************************************************************************************/ + +//this function will leave all bits of nodes set to 1 +static inline +void initialize_map_aux(SddNode* node, SddLiteral* variable_map) { + if (node->bit) return; //node visited before + //this is the first visit for this node + node->bit=1; + + node->map = NULL; //default + + if(node->type==FALSE || node->type==TRUE) node->map = node; + else if(node->type==LITERAL) { + SddLiteral var = VAR_OF(node); + if(variable_map[var]==var) node->map = node; + } + else { //decomposition + int rename = 0; + FOR_each_prime_sub_of_node(prime,sub,node,{ + initialize_map_aux(prime,variable_map); + initialize_map_aux(sub,variable_map); + rename = rename || prime->map==NULL || sub->map==NULL; + }); + if(rename==0) node->map = node; + } +} + +static +void initialize_map(SddNode* node, SddLiteral* variable_map) { + initialize_map_aux(node,variable_map); + sdd_clear_node_bits(node); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/sdds/size.c b/pysdd/lib/libsdd-2.0/src/sdds/size.c new file mode 100644 index 0000000..b94272f --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/sdds/size.c @@ -0,0 +1,65 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +static SddSize sdd_size_leave_bits_1(SddNode* node); + +/**************************************************************************************** + * size of sdd + ****************************************************************************************/ + +//compute size of the sdd (sum of its decomposition sizes) +SddSize sdd_size(SddNode* node) { + CHECK_ERROR(GC_NODE(node),ERR_MSG_GC,"sdd_size"); + assert(!GC_NODE(node)); + + SddSize size = sdd_size_leave_bits_1(node); + sdd_clear_node_bits(node); + return size; +} + +SddSize sdd_size_leave_bits_1(SddNode* node) { + if (node->bit) return 0; + node->bit = 1; + + SddNodeSize size = 0; + if(IS_DECOMPOSITION(node)) { + size = node->size; + FOR_each_prime_sub_of_node(prime,sub,node,{ + size += sdd_size_leave_bits_1(prime); + size += sdd_size_leave_bits_1(sub); + }); + } + return size; +} + + +/**************************************************************************************** + * size of multi-rooted sdd + ****************************************************************************************/ + +//compute size of the sdd (sum of its decomposition sizes) +SddSize sdd_shared_size(SddNode** nodes, SddSize count) { + + SddSize size = 0; + + for(SddSize i=0; inode_wmcs[n->index]) +//derivative of a node (n is sdd node, m is wmc_manager) +#define DRV(n,m) (m->node_derivatives[n->index]) +//wmc of true (v is vtree, m is wmc_manager) +#define USED_TRUE_WMC(v,m) (m->used_true_wmcs[v->position]) +#define UNUSED_TRUE_WMC(v,m) (m->unused_true_wmcs[v->position]) + +//increment node derivative +#define INC_NODE_DRV(n,d,m) INC(m->node_derivatives[n->index],d) +//increment literal derivative +#define INC_LIT_DRV(l,d,m) INC(m->literal_derivatives[l],d) + + +/**************************************************************************************** + * WMC manager properties: macro utilities + ****************************************************************************************/ + +//properties of a wmc manager which are inherited from an sdd manager +#define ROOT(m) (m->sdd_manager->vtree) +#define VAR_COUNT(m) (m->sdd_manager->var_count) + +/**************************************************************************************** + * interface functions for weighted model count + ****************************************************************************************/ + +WmcManager* wmc_manager_new(SddNode* node, int lm, SddManager* manager) { + WmcManager* wmc_manager; + MALLOC(wmc_manager,WmcManager,"wmc_manager_new"); + + log_mode = lm; //declare mode + wmc_manager->log_mode = lm; //save mode + wmc_manager->node = node; + wmc_manager->sdd_manager = manager; + + //nodes are sorted so children appear before parents in the array + //n->index contains the location of node n in the array + SddSize node_count; //how many nodes in the sdd + wmc_manager->nodes = sdd_topological_sort(node,&node_count); + wmc_manager->node_count = node_count; + + //save node indices since ->index field of an sdd node is used by many other + //operations such as conditioning, saving, etc + CALLOC(wmc_manager->node_indices,SddSize,node_count,"wmc_manager_new"); + for(SddSize i=0; inode_indices[i] = wmc_manager->nodes[i]->index; + + //allocate memory for node wmcs and derivatives + CALLOC(wmc_manager->node_wmcs,SddWmc,node_count,"wmc_manager_new"); + CALLOC(wmc_manager->node_derivatives,SddWmc,node_count,"wmc_manager_new"); + + //allocate memory for literal wmcs and derivatives + SddLiteral literal_count = 2*manager->var_count +1; //one extra + CALLOC(wmc_manager->literal_weights,SddWmc,literal_count,"wmc_manager_new"); + CALLOC(wmc_manager->literal_derivatives,SddWmc,literal_count,"wmc_manager_new"); + //initialize literal weights + for(SddLiteral i=0; iliteral_weights[i] = ONEW; + + //for better literal indexing + wmc_manager->literal_weights += manager->var_count; + wmc_manager->literal_derivatives += manager->var_count; + + //allocate memory for the wmcs of true (with respect to each vtree node) + SddLiteral vtree_count = 2*manager->var_count -1; + CALLOC(wmc_manager->used_true_wmcs,SddWmc,vtree_count,"wmc_manager_new"); + CALLOC(wmc_manager->unused_true_wmcs,SddWmc,vtree_count,"wmc_manager_new"); + + return wmc_manager; +} + + +void wmc_manager_free(WmcManager* wmc_manager) { + free(wmc_manager->nodes); + free(wmc_manager->node_indices); + free(wmc_manager->node_wmcs); + free(wmc_manager->node_derivatives); + free(wmc_manager->literal_weights-VAR_COUNT(wmc_manager)); + free(wmc_manager->literal_derivatives-VAR_COUNT(wmc_manager)); + free(wmc_manager->used_true_wmcs); + free(wmc_manager->unused_true_wmcs); + free(wmc_manager); +} + +//returns a zero weight appropriate domain +SddWmc wmc_zero_weight(WmcManager* wmc_manager) { + log_mode = wmc_manager->log_mode; + return ZEROW; +} + +//returns a one weight in appropriate domain +SddWmc wmc_one_weight(WmcManager* wmc_manager) { + log_mode = wmc_manager->log_mode; + return ONEW; +} + +//sets the weight of a literal +//literal is an integer <> 0 +void wmc_set_literal_weight(const SddLiteral literal, const SddWmc weight, WmcManager* wmc_manager) { + wmc_manager->literal_weights[literal] = weight; +} + +//returns the weight of a literal +//literal is an integer <> 0 +SddWmc wmc_literal_weight(const SddLiteral literal, const WmcManager* wmc_manager) { + return wmc_manager->literal_weights[literal]; +} + +//returns the derivative of a literal +//literal is an integer <> 0 +SddWmc wmc_literal_derivative(const SddLiteral literal, const WmcManager* wmc_manager) { + return wmc_manager->literal_derivatives[literal]; +} + +//returns the marginal wmc of a literal +//literal is an integer <> 0 +SddWmc wmc_literal_pr(const SddLiteral literal, const WmcManager* wmc_manager) { + log_mode = wmc_manager->log_mode; + return DIV(MULT(wmc_manager->literal_derivatives[literal], + wmc_manager->literal_weights[literal]), + wmc_manager->wmc); +} + +/**************************************************************************************** + * computing weighted model count and sets derivatives of literals as a side effect + * + * literal weights can be set using wmc_set_literal_weight() + * literal derivaties can be recovered using wmc_literal_derivative() + * + ****************************************************************************************/ + +static inline +void initialize_wmc(WmcManager* wmc_manager) { + + //recover node indices in case they were changed by other operations + for(SddSize i=0; inode_count; i++) { + wmc_manager->nodes[i]->index = wmc_manager->node_indices[i]; + } + + //initialize derivatives + for(SddSize i=0; inode_count; i++) wmc_manager->node_derivatives[i] = ZEROW; + for(SddLiteral i=1; i<=VAR_COUNT(wmc_manager); i++) { + wmc_manager->literal_derivatives[i] = ZEROW; + wmc_manager->literal_derivatives[-i] = ZEROW; + } + + //declare used/unused variables + set_sdd_variables(wmc_manager->node,wmc_manager->sdd_manager); + + //compute true constants for used/unsused + cache_true_wmcs(ROOT(wmc_manager),wmc_manager); +} + + +//NOTE: when node is trivial, we don't know the vtree for which the node is normalized +//we assume that it is normalized for the vtree root (manager->vtree) +SddWmc wmc_propagate(WmcManager* wmc_manager) { + + //set mode + log_mode = wmc_manager->log_mode; + SddNode* node = wmc_manager->node; //root of sdd + SddNode** nodes = wmc_manager->nodes; //sorted nodes of sdd + Vtree* root = ROOT(wmc_manager); + + //INITIALIZE + initialize_wmc(wmc_manager); + + //the following assumes that a trivial node is normalized for the vtree root + if(node->type==FALSE) return wmc_manager->wmc = ZEROW; //all derivatives are ZEROW + if(node->type==TRUE) { //all variables are unused + SddWmc wmc = UNUSED_TRUE_WMC(root,wmc_manager); + update_derivatives_of_unused(ONEW,root,wmc_manager); + return wmc_manager->wmc = wmc; + } + + //FIRST PASS + + //compute weighted model counts + SddWmc wmc = ZEROW; //to avoid compiler warning + SddSize i = wmc_manager->node_count; + + while(i--) { //visit children before parents + SddNode* n = *nodes++; + if(n->type==FALSE) wmc = ZEROW; + else if(n->type==TRUE) wmc = ONEW; //trick! + else if(n->type==LITERAL) wmc = wmc_literal_weight(LITERAL_OF(n),wmc_manager); + else { //decomposition + Vtree* left = n->vtree->left; + Vtree* right = n->vtree->right; + wmc = ZEROW; + FOR_each_prime_sub_of_node(prime,sub,n,{ + SddWmc prime_wmc = WMC(prime,wmc_manager); + SddWmc sub_wmc = WMC(sub,wmc_manager); + if(!IS_ZEROW(prime_wmc) && !IS_ZEROW(sub_wmc)) { + //assuming gaps cannot be ZEROW + prime_wmc = wmc_of_missing(prime_wmc,left,prime->vtree,wmc_manager); + sub_wmc = wmc_of_missing(sub_wmc,right,sub->vtree,wmc_manager); + assert(!IS_ZEROW(prime_wmc) && !IS_ZEROW(sub_wmc)); + INC(wmc,MULT(prime_wmc,sub_wmc)); + } + }); + } + //save weighted model count + WMC(n,wmc_manager) = wmc; + } + + //wmc of node over used variables + SddWmc node_wmc = WMC(node,wmc_manager); + //wmc of true over unused variables + SddWmc unused_wmc = UNUSED_TRUE_WMC(root,wmc_manager); + //wmc of node over all variables + wmc = MULT(node_wmc,unused_wmc); + + //SECOND PASS + + //compute derivatives for unused variables (if any) + update_derivatives_of_unused(node_wmc,root,wmc_manager); + + //compute derivatives for variables inside node->vtree + i = wmc_manager->node_count; + DRV(node,wmc_manager) = unused_wmc; //root of sdd + + while(i--) { //visit parents before children + SddNode* n = *(--nodes); + SddWmc drv = DRV(n,wmc_manager); + if(IS_ZEROW(drv)) continue; //no update for derivatives + //ignoring true and false nodes + //false nodes do not affect derivatives + //true nodes are handled implicitly by update_derivative_gap + if(n->type==LITERAL) INC_LIT_DRV(LITERAL_OF(n),drv,wmc_manager); + else if(n->type==DECOMPOSITION) { //propagate derivative downwards + Vtree* left = n->vtree->left; + Vtree* right = n->vtree->right; + FOR_each_prime_sub_of_node(prime,sub,n,{ + SddWmc prime_wmc = WMC(prime,wmc_manager); + SddWmc sub_wmc = WMC(sub,wmc_manager); + if(!IS_ZEROW(prime_wmc) || !IS_ZEROW(sub_wmc)) { //otherwise, no derivative update + SddWmc prime_wmc_gap = wmc_of_missing(ONEW,left,prime->vtree,wmc_manager); + SddWmc sub_wmc_gap = wmc_of_missing(ONEW,right,sub->vtree,wmc_manager); + assert(!IS_ZEROW(prime_wmc_gap) && !IS_ZEROW(sub_wmc_gap)); + SddWmc product = MULT(drv,MULT(prime_wmc_gap,sub_wmc_gap)); + assert(!IS_ZEROW(product)); + if(!IS_ZEROW(prime_wmc)) INC_NODE_DRV(sub,MULT(prime_wmc,product),wmc_manager); + if(!IS_ZEROW(sub_wmc)) INC_NODE_DRV(prime,MULT(sub_wmc,product),wmc_manager); + if(!IS_ZEROW(prime_wmc) && !IS_ZEROW(sub_wmc)) { + product = MULT(drv,MULT(prime_wmc,sub_wmc)); + assert(!IS_ZEROW(product)); + update_derivatives_of_missing(MULT(product,sub_wmc_gap),left,prime->vtree,wmc_manager); + update_derivatives_of_missing(MULT(product,prime_wmc_gap),right,sub->vtree,wmc_manager); + } + } + }); + } + } + + return wmc_manager->wmc = wmc; +} + + +/**************************************************************************************** + * computing (and caching) wmc of true over used and unused variables + * + * a variable is + * --USED if it is referenced by the SDD of a WMC manager; + * --UNUSED otherwise + ****************************************************************************************/ + +static +void cache_true_wmcs(Vtree* vtree, WmcManager* wmc_manager) { + if(LEAF(vtree)) { + SddLiteral var = vtree->var; + SddWmc pw = wmc_literal_weight(var,wmc_manager); + SddWmc nw = wmc_literal_weight(-var,wmc_manager); + SddWmc sum = ADD(pw,nw); + assert(!IS_ZEROW(sum)); + if(vtree->all_vars_in_sdd) { //used var + USED_TRUE_WMC(vtree,wmc_manager) = sum; + UNUSED_TRUE_WMC(vtree,wmc_manager) = ONEW; + } + else { //unused var + USED_TRUE_WMC(vtree,wmc_manager) = ONEW; + UNUSED_TRUE_WMC(vtree,wmc_manager) = sum; + } + } + else { + cache_true_wmcs(vtree->left,wmc_manager); + cache_true_wmcs(vtree->right,wmc_manager); + + SddWmc l = USED_TRUE_WMC(vtree->left,wmc_manager); + SddWmc r = USED_TRUE_WMC(vtree->right,wmc_manager); + USED_TRUE_WMC(vtree,wmc_manager) = MULT(l,r); + + l = UNUSED_TRUE_WMC(vtree->left,wmc_manager); + r = UNUSED_TRUE_WMC(vtree->right,wmc_manager); + UNUSED_TRUE_WMC(vtree,wmc_manager) = MULT(l,r); + } +} + +/**************************************************************************************** + * computing wmc of true over used variables in vtree, but not in sub-vtree (could be NULL) + * + * this is needed to handle + * --true subs: depends on where they appear (i.e., in which decomposition node) + * --primes not normalized for vtree->left: gap between vtree->right and sub->vtree + * --subs not normalized for vtree->right: gap between vtree->right and sub->vtree + * + ****************************************************************************************/ + +static +SddWmc wmc_of_missing(SddWmc wmc, Vtree* vtree, Vtree* sub_vtree, WmcManager* wmc_manager) { + assert(!IS_ZEROW(wmc)); + + wmc = MULT(wmc,USED_TRUE_WMC(vtree,wmc_manager)); + if(sub_vtree!=NULL) wmc = DIV(wmc,USED_TRUE_WMC(sub_vtree,wmc_manager)); + + return wmc; +} + +/**************************************************************************************** + * update the derivatives of: + * --all used variables in vtree but not in sub-vtree (could be NULL) + * --all unused variables in vtree + * + * the first computation is needed to handle + * --true subs: depends on where they appear (i.e., in which decomposition node) + * --primes not normalized for vtree->left: gap between vtree->right and sub->vtree + * --subs not normalized for vtree->right: gap between vtree->right and sub->vtree + * + ****************************************************************************************/ + +//update the derivates of all USED variables in vtree, but not in sub_vtree (could be NULL) +static +void update_derivatives_of_missing(SddWmc drv_wmc, Vtree* vtree, Vtree* sub_vtree, WmcManager* wmc_manager) { + assert(!IS_ZEROW(drv_wmc)); + + if(vtree==sub_vtree || vtree->no_var_in_sdd) return; + else if(LEAF(vtree)) { + SddLiteral var = vtree->var; //must be used + INC_LIT_DRV(var,drv_wmc,wmc_manager); + INC_LIT_DRV(-var,drv_wmc,wmc_manager); + } + else { + SddWmc l_wmc = MULT(drv_wmc,USED_TRUE_WMC(vtree->left,wmc_manager)); + SddWmc r_wmc = MULT(drv_wmc,USED_TRUE_WMC(vtree->right,wmc_manager)); + + if(sub_vtree!=NULL && sdd_vtree_is_sub(sub_vtree,vtree)) { + SddWmc s_wmc = USED_TRUE_WMC(sub_vtree,wmc_manager); + if(sdd_vtree_is_sub(sub_vtree,vtree->left)) l_wmc = DIV(l_wmc,s_wmc); + else r_wmc = DIV(r_wmc,s_wmc); + } + + update_derivatives_of_missing(r_wmc,vtree->left,sub_vtree,wmc_manager); + update_derivatives_of_missing(l_wmc,vtree->right,sub_vtree,wmc_manager); + } +} + +//update the derivates of all UNUSED variables in vtree +static +void update_derivatives_of_unused(SddWmc drv_wmc, Vtree* vtree, WmcManager* wmc_manager) { + assert(!IS_ZEROW(drv_wmc)); + + if(vtree->all_vars_in_sdd==0) { + if(LEAF(vtree)) { + SddLiteral var = vtree->var; //must be unused + INC_LIT_DRV(var,drv_wmc,wmc_manager); + INC_LIT_DRV(-var,drv_wmc,wmc_manager); + } + else { + SddWmc l_wmc = UNUSED_TRUE_WMC(vtree->left,wmc_manager); + SddWmc r_wmc = UNUSED_TRUE_WMC(vtree->right,wmc_manager); + update_derivatives_of_unused(MULT(r_wmc,drv_wmc),vtree->left,wmc_manager); + update_derivatives_of_unused(MULT(l_wmc,drv_wmc),vtree->right,wmc_manager); + } + } +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/util.c b/pysdd/lib/libsdd-2.0/src/util.c new file mode 100644 index 0000000..4a39751 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/util.c @@ -0,0 +1,255 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include +#include "sdd.h" + + +void press(char* str) { + printf("\n%s, press enter?",str); fflush(stdout); + getchar(); +} + +/**************************************************************************************** + * printing stack when assertion fails + ****************************************************************************************/ + +#define CALLS_COUNT 11 +//prints the last CALLS_COUNT-1 calls, excluding print_trace() +void print_trace () { + /* + void *array[CALLS_COUNT]; + int size; + char **strings; + + size = backtrace(array,CALLS_COUNT); + strings = backtrace_symbols(array,size); + + printf("\n"); + for (int i=0; ivar_count==1); + VERIFY(v==v->first); + VERIFY(v==v->last); + }); + + //verify internal nodes + FOR_each_internal_vtree_node(v,vtree,{ + VERIFY(LEAF(v->first)); + VERIFY(LEAF(v->last)); + VERIFY(v->first==v->left->first); + VERIFY(v->last==v->right->last); + VERIFY(v->left->last->next==v); + VERIFY(v->right->first->prev==v); + VERIFY(v->prev==v->left->last); + VERIFY(v->next==v->right->first); + VERIFY(v->first->prev==NULL || v->first->prev->next==v->first); + VERIFY(v->last->next==NULL || v->last->next->prev==v->last); + VERIFY(v->var_count==v->left->var_count+v->right->var_count); + VERIFY(v->first->position < v->last->position); + VERIFY(v->position>v->first->position); + VERIFY(v->positionlast->position); + VERIFY((v->last->position-v->first->position+1)==(2*v->var_count -1)); + }); + + return 1; +} + +static +int verify_X_constrained_aux(const Vtree* vtree) { + if(LEAF(vtree)) return vtree->some_X_constrained_vars; + else { + int l = verify_X_constrained_aux(vtree->left); + int r = verify_X_constrained_aux(vtree->right); + VERIFY(l || r || vtree->some_X_constrained_vars==0); + return vtree->some_X_constrained_vars; + } +} + +int verify_X_constrained(const Vtree* vtree) { + verify_X_constrained_aux(vtree); + + const Vtree* r = vtree; + while(INTERNAL(r) && r->some_X_constrained_vars) r = r->right; + //if r is root, then there are no x constrained variables + + VERIFY(r->some_X_constrained_vars==0); + + FOR_each_vtree_node(v,vtree,{ + VERIFY(v->some_X_constrained_vars || sdd_vtree_is_sub(v,r)); + }); + + return 1; +} + +/**************************************************************************************** + * counts ans sizes + * + * dead/live counts and sizes match between vtrees, manager, and normalized node lists + ****************************************************************************************/ + +int verify_counts_and_sizes(const SddManager* manager) { + + SddSize vtree_count = 0; + SddSize vtree_dead_count = 0; + SddSize vtree_size = 0; + SddSize vtree_dead_size = 0; + + FOR_each_internal_vtree_node(v,manager->vtree,{ + VERIFY(v->node_count >= v->dead_node_count); + + SddSize live_count = 0; + SddSize dead_count = 0; + SddSize live_size = 0; + SddSize dead_size = 0; + + FOR_each_sdd_node_normalized_for(n,v,{ //iterating over nodes n normalized for vtree node v + if(n->ref_count) { //live node + ++live_count; + live_size += n->size; + } + else { //dead node + ++dead_count; + dead_size += n->size; + } + }); + + VERIFY(v->node_count==live_count+dead_count); + VERIFY(v->dead_node_count==dead_count); + VERIFY(v->sdd_size==live_size+dead_size); + VERIFY(v->dead_sdd_size==dead_size); + + vtree_count += live_count+dead_count; + vtree_dead_count += dead_count; + vtree_size += live_size+dead_size; + vtree_dead_size += dead_size; + }); + + VERIFY(manager->node_count==vtree_count); + VERIFY(manager->dead_node_count==vtree_dead_count); + VERIFY(manager->sdd_size==vtree_size); + VERIFY(manager->dead_sdd_size==vtree_dead_size); + + VERIFY(manager->node_count==sdd_vtree_count(manager->vtree)); + VERIFY(manager->dead_node_count==sdd_vtree_dead_count(manager->vtree)); + VERIFY(manager->sdd_size==sdd_vtree_size(manager->vtree)); + VERIFY(manager->dead_sdd_size==sdd_vtree_dead_size(manager->vtree)); + + return 1; +} + +/**************************************************************************************** + * normalization + * + * nodes normalized for correct vtree + ****************************************************************************************/ + +int verify_normalization(const SddManager* manager) { + Vtree* root = manager->vtree; + FOR_each_internal_vtree_node(v,root, + FOR_each_sdd_node_normalized_for(node,v,VERIFY(node->vtree==v))); + FOR_each_decomposition_in(n,root,{ + Vtree* l = NULL; + Vtree* r = NULL; + FOR_each_prime_sub_of_node(p,s,n,{ + if(p->vtree) { + if(l) l=sdd_vtree_lca(p->vtree,l,root); else l=p->vtree; + } + if(s->vtree) { + if(r) r=sdd_vtree_lca(s->vtree,r,root); else r=s->vtree; + } + }); + VERIFY(sdd_vtree_is_sub(l,n->vtree->left)); + VERIFY(sdd_vtree_is_sub(r,n->vtree->right)); + VERIFY(n->vtree==sdd_vtree_lca(l,r,root)); + }); + return 1; +} + +/**************************************************************************************** + * negations + * + * nodes and their negations point to each other + * nodes and their negations normalized for same vtree + ****************************************************************************************/ + +int verify_negations(const SddManager* manager) { + FOR_each_decomposition_in(n,manager->vtree,{ + if(n->negation) { + VERIFY(!GC_NODE(n->negation)); + VERIFY(n==n->negation->negation); + VERIFY(n->vtree==n->negation->vtree); + } + }); + return 1; +} + +/**************************************************************************************** + * garbage collection + ****************************************************************************************/ + +static +int check_gc_at(const Vtree *vtree) { + VERIFY(vtree->dead_node_count==0); + FOR_each_sdd_node_normalized_for(n,vtree,{ + VERIFY(n->ref_count); + VERIFY(!GC_NODE(n)); + FOR_each_prime_sub_of_node(p,s,n,{ + VERIFY(p->parent_count); + VERIFY(s->parent_count); + }); + }); + return 1; +} + +static +int check_gc_above(const Vtree* vtree) { + FOR_each_ancestral_vtree_node(v,vtree,if(!check_gc_at(v)) return 0); + return 1; +} + +static +int check_gc_in(const Vtree* vtree) { + FOR_each_internal_vtree_node(v,vtree,if(!check_gc_at(v)) return 0); + return 1; +} + +//checks coherence of garbage collector +int verify_gc(const Vtree* vtree, SddManager* manager) { + VERIFY(check_gc_in(vtree)); + VERIFY(check_gc_above(vtree)); + + //verifying parent_count + FOR_each_unique_node(n,manager,n->index=0); + FOR_each_unique_node(n,manager,{ + FOR_each_prime_sub_of_node(p,s,n,{ + ++p->index; + ++s->index; + }); + }); + FOR_each_unique_node(n,manager,VERIFY(n->index==n->parent_count)); + + return 1; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/version.c b/pysdd/lib/libsdd-2.0/src/version.c new file mode 100644 index 0000000..bf7dfee --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/version.c @@ -0,0 +1,14 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +const char* libsdd_version() { + return "libsdd version 2.0"; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c b/pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c new file mode 100644 index 0000000..4fc5112 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_fragments/construction.c @@ -0,0 +1,329 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/shadows.c +SddShadows* shadows_new(SddSize root_count, SddNode** root_nodes, SddManager* manager); +void shadows_free(SddShadows* shadows); + +//vtree_fragments/operations.c +int valid_fragment_initial_state(VtreeFragment* fragment); + +//local declarations +void initialize_sdd_dag(SddSize root_count, SddNode** root_nodes, SddSize changeable_count, SddNode** changeable_nodes); + +/**************************************************************************************** + * A "fragment" is a pair of vtree nodes (x y), where + * + * --x is the parent of y, and + * --y is not a leaf vtree node + * + * A fragment is either: + * + * --left-linear, having the form x=(y=(a b) c) + * --right-linear, having the form x=(a y=(b c)) + * + * vtree nodes (x y) are called the fragment's internal nodes, while + * vtree nodes (a b c) are called the fragment's leaves (not necessarily vtree leaves) + * + * For internal nodes (x y) and leave nodes (a b c), there are 12 fragments states, + * which correspond to the 2 permutations of (x y) and 6 permutations of (a b c): + * + * --6 right-linear: + * (a (b c)), (a (c b)), (b (a c)), (b (c a)), (c (a b)), (c (b a)) + * --6 left-linear : + * ((a b) c), ((a c) b), ((b a) c), ((b c) a), ((c a) b), ((c b) a) + * + * One can cycle through these fragment states using only three vtree operations: + * left-rotate child, right-rotate root, swap child. + * + * + * A number of utilities are provided for cycling through fragment states: + * + * -- vtree_fragment_new(x,y): + * constructs a fragment structure with internal nodes (x y) (at initial state) + * + * -- vtree_fragment_free(fragment): + * frees a fragment structure + * + * -- vtree_fragment_forward(fragment): + * tries to advance the fragment to its next state using limited vtree operations + * (may fail due to time/space limits) + * + * -- vtree_fragment_rewind(fragment): + * brings a fragment back to its initial state + * + * -- vtree_fragment_goto(target-state,fragment): + * moves a fragment from its initial state to target-state + * + * The typical use of the above functions is as follows: + * + * --one creates a fragment using the _new function, then tries to cycle through + * its states using the _forward function + * + * --if cycling through all 12 states succeeds, then one can move to any of these states + * using the _goto function + * + * --if cycling fails (due to time/space limits), one goes back to the initial state + * using the _rewind function + * + * --one finally dispenses of the fragment structure using the _free function + ****************************************************************************************/ + +//vtree moves which allow one to cycle through all 12 states of a fragment +//left-rotation applies to child +//right-rotation applies to root +//swapping applies to child + +//starting from a left-linear state +static char moves_ll[12] = {'r','s','l','s', 'r','s','l','s', 'r','s','l','s'}; +//the following order of navigation is much worse +//static char moves_ll[12] = {'s','r','s','l', 's','r','s','l', 's','r','s','l'}; + +//starting from a right-linear state +static char moves_rl[12] = {'l','s','r','s', 'l','s','r','s', 'l','s','r','s'}; +//the following order of navigation is much worse +//static char moves_rl[12] = {'s','l','s','r', 's','l','s','r', 's','l','s','r'}; + +//note: the 6 permutations of leaves (a b c) are generated after moves 1, 3, 5, 7, 9, 11 + +/**************************************************************************************** + * Consider a fragment for root and child, in its initial state. + * + * The fragment defines an sdd DAG (also called the fragment DAG) whose roots are: + * + * --IR: sdd nodes normalized for root + * --IC: sdd nodes normalized for child and having no parents at root + * + * An sdd node that belongs to the fragment DAG is said to be INSIDE the fragment, or + * a fragment node. Otherwise, the node is said to be OUTSIDE the fragment. + * + * When a fragment is constructed, there are no dead nodes inside or above root. + * Hence, all fragment nodes must be live. + * + * Moreover, if n is a fragment node, then + * + * --node m is a FRAGMENT PARENT of n iff m is a parent of n and m is a fragment node. + * --node n has an EXTERNAL reference iff its reference count (n->ref_count) is greater + * than the number of its fragment parents. + * + * Note that nodes IR+IC must all have external references. Moreover, a fragment + * structure saves IR+IC nodes in an array (fragment->root_nodes). + * + * Another category of fragments nodes is: + * + * --Ic: sdd nodes normalized for child, with parents at root and external references. + * + * Nodes Ic are also saved in the fragment structure (fragment-> + * + * Nodes IR+IC+Ic cover all nodes at root and child, except for nodes at child that have + * have no external references (these must have parents at root since they are live). + * + * IR+IC are important because they define the fragment DAG. Moreover, node IR+IC+Ic are + * important because they constitute all nodes at root and child that also have + * external references (these nodes must preserve their structures). + ****************************************************************************************/ + +/**************************************************************************************** + * freeing a fragment + ****************************************************************************************/ + +void vtree_fragment_free(VtreeFragment* fragment) { + if(fragment->shadows) shadows_free(fragment->shadows); //shadows may have not been constructed + free(fragment->IR_IC_nodes); + free(fragment->Ic_nodes); + free(fragment); +} + +/**************************************************************************************** + * Identifying nodes IC and Ic + ****************************************************************************************/ + +//for each child node n, set n->index to the number of parents that n has at root +void count_internal_parents_of_child_nodes(Vtree* root, Vtree* child) { + FOR_each_sdd_node_normalized_for(node,child,node->index=0); + FOR_each_sdd_node_normalized_for(node,root, + FOR_each_prime_sub_of_node(prime,sub,node,++prime->index;++sub->index)); + //for child node n, n->index is now the number of its parents at root +} + +//classification of sdd nodes normalized for the child of a fragment (Id are used in operations.c) +//n in IC iff n->index==0 (no parents at root) +//n in Ic iff n->index>0 (parents at root) and n->ref_count>n->index (external references) +//n in Id iff n->index>0 (parents at root) and n->ref_count==n->index (no external references) +//these macros should be called only after calling count_internal_parents_of_child_nodes() +#define IN_IC(N) (N->index==0) +#define IN_Ic(N) (N->index>0 && N->ref_count>N->index) + +/**************************************************************************************** + * constructing a fragment + ****************************************************************************************/ + +//type 'r': right-linear fragment +//type 'l': left-linear fragment +VtreeFragment* vtree_fragment_new(Vtree* root, Vtree* child, SddManager* manager) { + + VtreeFragment* fragment; + MALLOC(fragment,VtreeFragment,"vtree_fragment_new"); + + fragment->manager = manager; + fragment->type = (child==root->right? 'r': 'l'); + fragment->root = root; + fragment->child = child; + fragment->moves = (fragment->type=='r'? moves_rl: moves_ll); + fragment->shadows = NULL; + + fragment->state = 0; //state + fragment->mode = 'i'; //state + fragment->cur_root = root; //state + fragment->cur_child = child; //state + + //counting IC and Ic nodes + count_internal_parents_of_child_nodes(root,child); + SddSize IC_count = 0; //child nodes with no parents at root + SddSize Ic_count = 0; //child nodes with parents at root and external references + FOR_each_sdd_node_normalized_for(node,child,{ + if(IN_IC(node)) ++IC_count; + else if(IN_Ic(node)) ++Ic_count; + }); + + fragment->IR_IC_nodes = NULL; + fragment->Ic_nodes = NULL; + fragment->IR_IC_count = root->node_count + IC_count; + fragment->Ic_count = Ic_count; + assert(fragment->IR_IC_count!=0 || fragment->Ic_count==0); + + if(fragment->IR_IC_count==0) return fragment; + + //allocate array to hold roots of the fragment DAG + CALLOC(fragment->IR_IC_nodes,SddNode*,fragment->IR_IC_count,"vtree_fragment_new"); + //allocate array to hold Ic nodes + CALLOC(fragment->Ic_nodes,SddNode*,fragment->Ic_count,"vtree_fragment_new"); + + //save IR nodes + FOR_each_sdd_node_normalized_for(node,root,*fragment->IR_IC_nodes++ = node); + //save IC and Ic nodes + FOR_each_sdd_node_normalized_for(node,child,{ + if(IN_IC(node)) *fragment->IR_IC_nodes++ = node; + else if(IN_Ic(node)) *fragment->Ic_nodes++ = node; + }); + fragment->IR_IC_nodes -= fragment->IR_IC_count; + fragment->Ic_nodes -= fragment->Ic_count; + + assert(valid_fragment_initial_state(fragment)); + + return fragment; +} + +/**************************************************************************************** + * constructing and freeing fragment shadows + ****************************************************************************************/ + + //construct +void construct_fragment_shadows(VtreeFragment* fragment) { + assert(valid_fragment_initial_state(fragment)); + + SddManager* manager = fragment->manager; + SddSize IR_IC_count = fragment->IR_IC_count; + SddNode** IR_IC_nodes = fragment->IR_IC_nodes; + SddSize Ic_count = fragment->Ic_count; + SddNode** Ic_nodes = fragment->Ic_nodes; + + //set node->shadow_types and initialize node->shadow=NULL + initialize_sdd_dag(IR_IC_count,IR_IC_nodes,Ic_count,Ic_nodes); + + SddShadows* shadows = fragment->shadows + = shadows_new(IR_IC_count,IR_IC_nodes,manager); + + //keeping track of shadows stats + manager->max_fragment_shadow_count = MAX(shadows->shadow_count,manager->max_fragment_shadow_count); + manager->max_fragment_shadow_byte_count = MAX(shadows->shadow_byte_count,manager->max_fragment_shadow_byte_count); +} + +//free +void free_fragment_shadows(VtreeFragment* fragment) { + assert(fragment->shadows); //shadows have been constructed + shadows_free(fragment->shadows); + fragment->shadows = NULL; +} + +/**************************************************************************************** + * initializing nodes in the sdd-DAG and setting their shadow types + * + * A node n in the sdd DAG has an external reference iff + * + * n->ref_count > pcount + * + * where pcount is the number of internal parents of n (i.e., parents in the sdd DAG). + ****************************************************************************************/ + +//will visit all nodes in the sdd-DAG EXCEPT its roots +static +void initialize(SddNode* node) { + if(node->bit) ++node->index; //node visited before + else { //first visit to node + node->bit = 1; + node->index = 1; + node->shadow = NULL; + node->shadow_type = '?'; + if(node->type==DECOMPOSITION) { + FOR_each_prime_sub_of_node(prime,sub,node,initialize(prime);initialize(sub)); + } + } +} + +//will visit all nodes in the sdd-DAG EXCEPT its roots +static +void set_shadow_types(SddNode* node, int parent_is_terminal) { + assert(LIVE(node)); //node is live + assert(node->index); //node has internal parents + if(node->shadow_type=='?' || parent_is_terminal) { + //if node->shadow_type=='?', then first visit to node, node->index is number of internal parents, + //and node->ref_count>node->index iff node has external references + int terminal = node->type!=DECOMPOSITION || parent_is_terminal || node->ref_count>node->index; + if(terminal) node->shadow_type = 't'; //node will not be gc'd or its elements changed + else node->shadow_type = 'g'; //node may be gc'd + } + if(--node->index==0) { //last visit to node (type is now finalized) + node->bit = 0; //bits must be cleared + if(node->type==DECOMPOSITION) { + parent_is_terminal = node->shadow_type=='t'; + FOR_each_prime_sub_of_node(prime,sub,node,{ + set_shadow_types(prime,parent_is_terminal); + set_shadow_types(sub,parent_is_terminal); + }); + } + } +} + +void initialize_sdd_dag(SddSize root_count, SddNode** root_nodes, SddSize changeable_count, SddNode** changeable_nodes) { + //first pass: initialize shadows, their types, and count internal parents + for(SddSize i=0; iindex = 0; + node->shadow = NULL; + FOR_each_prime_sub_of_node(prime,sub,node,initialize(prime);initialize(sub)); + } + + for(SddSize i=0; iref_count && node->index); //node is live and non-root + node->shadow_type = 'c'; //node will not be gc'd but its elements may be changed + } + + //second pass: set shadow types and clear bits + for(SddSize i=0; iref_count && node->index==0); //node is live and root + node->shadow_type = 'c'; //node will not be gc'd but its elements may be changed + FOR_each_prime_sub_of_node(prime,sub,node,set_shadow_types(prime,0);set_shadow_types(sub,0)); + } +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c b/pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c new file mode 100644 index 0000000..7d0f921 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_fragments/moves.c @@ -0,0 +1,100 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * vtree move operations: + * + * --try_vtree_move (with limits) + * --make_vtree_move (without limits) + * --reverse_vtree_move (light, without adjusting sdd nodes) + * + * left-rotate : always apply to the fragment's current child + * right-rotate: always apply to fragment's current root + * swap : always applies to the fragment's current child + ****************************************************************************************/ + +//try move in given direction WITH limits +//if move succeeds, return 1, otherwise return 0 +int try_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager, int limited) { + assert(move=='l' || move=='r' || move=='s'); + + if(move=='l') { + assert(*child==sdd_vtree_right(*root)); + if(sdd_vtree_rotate_left(*child,manager,limited)) { + //left rotation succeeded + SWAP(Vtree*,*root,*child); //root/child flip positions + return 1; + } + } + else if(move=='r') { + assert(*child==sdd_vtree_left(*root)); + if(sdd_vtree_rotate_right(*root,manager,limited)) { + //right rotation succeeded + SWAP(Vtree*,*root,*child); //root/child flip positions + return 1; + } + } + else { + assert(move=='s'); + assert(*root==sdd_vtree_parent(*child)); + if(sdd_vtree_swap(*child,manager,limited)) { + //swap succeeded, root/child stay the same + return 1; + } + } + + return 0; //move failed +} + +//make move in given direction WITHOUT limits +//move will always succeed +void make_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager) { + assert(move=='l' || move=='r' || move=='s'); + + if(move=='l') { + assert(*child==sdd_vtree_right(*root)); + sdd_vtree_rotate_left(*child,manager,0); + SWAP(Vtree*,*root,*child); //root/child flip positions + } + else if(move=='r') { + assert(*child==sdd_vtree_left(*root)); + sdd_vtree_rotate_right(*root,manager,0); + SWAP(Vtree*,*root,*child); //root/child flip positions + } + else { + assert(move=='s'); + assert(*root==sdd_vtree_parent(*child)); + sdd_vtree_swap(*child,manager,0); //root/child stay the same + } + +} + +//reverse a vtree move without adjusting sdd nodes +void reverse_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager) { + + if(move=='r') { + assert(*child==sdd_vtree_right(*root)); + rotate_vtree_left(*child,manager); //light rotation + SWAP(Vtree*,*root,*child); //root/child flip positions + } + else if(move=='l') { + assert(*child==sdd_vtree_left(*root)); + rotate_vtree_right(*root,manager); //light rotation + SWAP(Vtree*,*root,*child); //root/child flip positions + } + else { //move=='s' + assert(*root==sdd_vtree_parent(*child)); + swap_vtree_children(*child,manager); //light swapping + //root/child stay the same + } + +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c b/pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c new file mode 100644 index 0000000..cbefdb2 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_fragments/operations.c @@ -0,0 +1,302 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/gc.c +void garbage_collect_in(Vtree* vtree, SddManager* manager); + +//basic/memory.c +void gc_sdd_node(SddNode* node, SddManager* manager); + +//basic/nodes.c +void remove_from_unique_table(SddNode* node, SddManager* manager); +void insert_in_unique_table(SddNode* node, SddManager* manager); + +//basic/shadows.c +void shadows_recover(SddShadows* shadows); + +//vtree_fragments/construction.c +void count_internal_parents_of_child_nodes(Vtree* root, Vtree* child); +void construct_fragment_shadows(VtreeFragment* fragment); +void free_fragment_shadows(VtreeFragment* fragment); + +//vtree_fragments/moves.c +int try_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager, int limited); +void make_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager); +void reverse_vtree_move(char move, Vtree** root, Vtree** child, SddManager* manager); + +//local declaration +static void recover_fragment_shadows(VtreeFragment* fragment); + +/**************************************************************************************** + * Fragment operations: next, goto and rewind + * + * A fragment can be in one of three modes: (i: initial) (n: next) (g: goto) + * + * A fragment is started in the initial mode. + * + * Operations are only applicable in certain modes: + * + * --the next operation can only be applied in the initial or next modes + * --the goto operation can only be applied in the initial or goto modes + * --the rewind operation can only by applied in the initial or next modes + * + * Operations change the fragment mode: + * + * --next puts the fragment in the next mode + * --goto puts the fragment in the goto mode + * --rewind puts the fragment in the initial mode + * + * Finally, shadows are only present in next mode (not in initial or goto modes) + ****************************************************************************************/ + +int vtree_fragment_state(VtreeFragment* fragment) { + return fragment->state; +} + +Vtree* vtree_fragment_root(VtreeFragment* fragment) { + return fragment->cur_root; +} + +//checks the validity of a fragment initial state +int valid_fragment_initial_state(VtreeFragment* fragment) { + + char type = fragment->type; + Vtree* root = fragment->root; + Vtree* child = fragment->child; + + return fragment->state==0 && + fragment->mode=='i' && + root==fragment->cur_root && + child==fragment->cur_child && + ((type=='r' && child==root->right) || (type=='l' && child==root->left)); +} + +//check whether fragment is in its initial state +int vtree_fragment_is_initial(VtreeFragment* fragment) { + + if(fragment->state==0) { //initial state + assert(valid_fragment_initial_state(fragment)); + return 1; + } + else { + assert((0 < fragment->state) && (fragment->state <= 11)); + assert(fragment->mode=='n' || fragment->mode=='g'); + return 0; + } +} + +/**************************************************************************************** + * utilities + ****************************************************************************************/ + +//returns the new state +static +int update_state(char direction, VtreeFragment* fragment) { + if(direction=='f') { //next state + if(++fragment->state==12) fragment->state = 0; + } + else { //previous state + assert(direction=='b'); + if(--fragment->state==-1) fragment->state = 11; + } + assert(0 <= fragment->state && fragment->state <= 11); + return fragment->state; //the new state +} + +//get move that will take us to a neighboring state +static +char get_move_to_neighbor(char direction, VtreeFragment* fragment) { + assert(direction=='f' || direction=='b'); + int state = fragment->state; + + if(direction=='f') return fragment->moves[state]; + + int prev_state = (state==0? 11: state-1); + char prev_move = fragment->moves[prev_state]; + + //reverse it + if(prev_move=='l') return 'r'; + else if(prev_move=='r') return 'l'; + else return 's'; +} + +/**************************************************************************************** + * Moving a fragment to a neighboring state (with limits). + ****************************************************************************************/ + +//moves the fragment to a neighboring state (WITH limits) +//returns 1 if move successful; returns 0 otherwise +//if move is not successful, fragment stays at its current state +int vtree_fragment_next(char direction, VtreeFragment* fragment, int limited) { + assert(0 <= fragment->state && fragment->state <= 11); + assert(direction=='f' || direction=='b'); + + //cannot be in goto mode + CHECK_ERROR(fragment->mode=='g',ERR_MSG_FRG_N,"vtree_fragment_next"); + + //shadows are needed to rewind + if(fragment->mode=='i') construct_fragment_shadows(fragment); + + char move = get_move_to_neighbor(direction,fragment); + int status; + + if(try_vtree_move(move,&fragment->cur_root,&fragment->cur_child,fragment->manager,limited)) { + update_state(direction,fragment); + status = 1; //success + } + else status = 0; //failure + + if(fragment->state==0) { //at initial state + fragment->mode = 'i'; //initial mode + free_fragment_shadows(fragment); //shadows no longer needed + assert(valid_fragment_initial_state(fragment)); + } + else fragment->mode = 'n'; //now in next mode + + return status; +} + +/**************************************************************************************** + * Moving a fragment to a particular state. + ****************************************************************************************/ + +//moves a fragment from the current state to the target state (WITHOUT limits) +//no shortcuts are used +//returns the fragment root at the target state +Vtree* vtree_fragment_goto(int state, char direction, VtreeFragment* fragment) { + assert(0 <= state && state <= 11); + assert(direction=='f' || direction=='b'); + + //cannot be in next mode + CHECK_ERROR(fragment->mode=='n',ERR_MSG_FRG_G,"vtree_fragment_goto"); + + while(fragment->state != state) { + char move = get_move_to_neighbor(direction,fragment); + make_vtree_move(move,&fragment->cur_root,&fragment->cur_child,fragment->manager); + update_state(direction,fragment); + } + + fragment->mode = (fragment->state==0? 'i': 'g'); + + return fragment->cur_root; +} + +/**************************************************************************************** + * Rewinding a fragment to its initial state (without using vtree operations). + ****************************************************************************************/ + +//moves the fragment to its initial state +//returns the fragment root at its initial state +Vtree* vtree_fragment_rewind(VtreeFragment* fragment) { + assert(0 <= fragment->state && fragment->state <= 11); + + if(fragment->mode=='i') { //already in initial state + assert(valid_fragment_initial_state(fragment)); + return fragment->root; + } + + //cannot be in goto mode + CHECK_ERROR(fragment->mode=='g',ERR_MSG_FRG_R,"vtree_fragment_rewind"); + + recover_fragment_shadows(fragment); + fragment->mode = 'i'; //now in initial mode + + assert(valid_fragment_initial_state(fragment)); + + return fragment->root; +} + +/**************************************************************************************** + * Recovering shadows to support fragment rewinding. + ****************************************************************************************/ + +//classification of sdd nodes normalized for the child of a fragment (IC and Ic are used in construction.c) +//n in IC iff n->index==0 (no parents at root) +//n in Ic iff n->index>0 (parents at root) and n->ref_count>n->index (external references) +//n in Id iff n->index>0 (parents at root) and n->ref_count==n->index (no external references) +//these macros should be called only after calling count_internal_parents_of_child_nodes() +#define IN_Id(N) (N->index>0 && N->ref_count==N->index) + +//recover +void recover_fragment_shadows(VtreeFragment* fragment) { + assert(fragment->shadows); //shadows have been constructed + + SddManager* manager = fragment->manager; + + //save current root and child + Vtree* prev_root = fragment->cur_root; + Vtree* prev_child = fragment->cur_child; + Vtree* prev_child_left = prev_child->left; + Vtree* prev_child_right = prev_child->right; + + //bring vtree back to its original state (without adjusting sdd nodes) + while(fragment->state > 0) { + char move = fragment->moves[--fragment->state]; + reverse_vtree_move(move,&fragment->cur_root,&fragment->cur_child,fragment->manager); + } + assert(fragment->state==0); + assert(fragment->root==fragment->cur_root && fragment->child==fragment->cur_child); + + //IR+IC+Ic nodes must appear at prev_root and prev_child (all have external references) + //nodes at prev_root all belong to IR+IC+Ic + //nodes at prev_child belong to one of two groups: + //--those that have external references (all must belong to IR+IC+Ic) + //--those have have no external references (called Id nodes, all of which must have parents at prev_root) + + //except for Id nodes, all nodes at prev_root and prev_child will be updated when recovering shadows + //(that is, their elements and vtrees will be updated to match the fragment's initial vtree) + //as for Id nodes: + //--they are valid in the fragment's initial vtree if cur_child and prev_child are the same + //--they are invalid otherwise + //if Id nodes are valid, we just make sure they are at the correct vtree node + //if Id nodes are invalid, they must be removed from cur_child and gc'd after recovering shadows + + SddNode* invalid_Id_nodes = NULL; + Vtree* cur_child = fragment->cur_child; + if(prev_child!=cur_child || prev_child_left!=cur_child->left || prev_child_right!=cur_child->right) { + //Id nodes are invalid or at the wrong vtree node + count_internal_parents_of_child_nodes(prev_root,prev_child); + SddNode* Id_nodes = NULL; + FOR_each_sdd_node_normalized_for(node,prev_child, + assert(node->ref_count!=node->index || node->index>0); //no external refs implies parents at prev_root + if(IN_Id(node)) { + remove_from_unique_table(node,manager); + node->next = Id_nodes; + Id_nodes = node; + } + ); + if(prev_child_left==cur_child->left && prev_child_right==cur_child->right) { + assert(prev_child!=cur_child); + //Id nodes are valid but at the wrong vtree node + FOR_each_linked_node(node,Id_nodes,{ + assert(node->vtree!=cur_child); + node->vtree = cur_child; + insert_in_unique_table(node,manager); + }); + } + else invalid_Id_nodes = Id_nodes; //Id nodes are invalid + } + + //recover sdd-DAG from shadow-DAG + shadows_recover(fragment->shadows); + fragment->shadows = NULL; //shadows are free after recovery + + //invalid_id_nodes may be NULL + FOR_each_linked_node(node,invalid_Id_nodes,assert(DEAD(node));gc_sdd_node(node,manager)); + + //no dead nodes initially above fragment, and no dead nodes will be created + //above fragment; hence, we only need to gc inside fragment + garbage_collect_in(fragment->cur_root,manager); + + assert(!FULL_DEBUG || verify_gc(fragment->cur_root,fragment->manager)); + assert(!FULL_DEBUG || verify_counts_and_sizes(fragment->manager)); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c new file mode 100644 index 0000000..479f1a1 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/cartesian_product.c @@ -0,0 +1,166 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/memory.c +SddElement* new_elements(SddNodeSize size, SddManager* manager); + +//basic/multiply.c +int multiply_decompositions(SddElement* elements1, SddNodeSize size1, SddElement* elements2, SddNodeSize size2, + BoolOp op, Vtree* vtree, SddManager* manager, int limited, void fn(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager* manager)); + +//sdds/apply.c +SddNode* apply(SddNode* node1, SddNode* node2, BoolOp op, SddManager* manager, int limited); + +/**************************************************************************************** + * + * compute the compressed cartesian product of a collection of sets: set1 x set2 x .... x setn + * + * --each set is a partition {(p,s)} + * --the product of two sets {(pi,si)} x {(pj,sj)} is {(pi*pj,si+si) | pi*pj<>false} + * --the cartesian product is a partition by definition + * --it will also be guaranteed to be compressed + * + * three stacks are used to manage the process: + * --stack1 will contain the current cartesian product + * --stack2 will contain the next defined partition + * --stack3 is used to multiple stack1 and stack2, compress the result, and then move back to stack1 + * + * the following functions are meant to assist in this computation: + * + * --open_cartesian_product(...) is called first to start the computation of a cartesian product + * --for each set: + * --open_partition(...) is called to declare the beginning of a set + * --declare_element_of_partition(p,s) is called to declare a member of a set + * --close_partition(...) is called when all members of the set have been declared + * --close_cartesian_product(...) is called to end the computation of a cartesian product + * + * observation: the size of a cartesian product cannot get smaller as a result of + * multiplying it with a partition. hence, the size of the cartesian product will be + * monotonically increasing as we add another set to the product + * + ****************************************************************************************/ + +//compresses stack3 and puts result in stack1 +//this is more efficient than relying on the compression utility in basic/partitions.c +#define COMPRESS_S3_to_S1(manager,limited) {\ + RESET_STACK(cp_stack1,manager);\ + sort_uncompressed_elements(STACK_SIZE(cp_stack3,manager),STACK_START(cp_stack3,manager));\ + /* equal subs are now adjacent */\ + POP_ELM(SddNode* prev_prime,SddNode* prev_sub,cp_stack3,manager);\ + while(!IS_STACK_EMPTY(cp_stack3,manager)) {\ + POP_ELM(SddNode* prime,SddNode* sub,cp_stack3,manager);\ + if(sub==prev_sub) {\ + /* apply below will not invoke gc or minimization since vtree_search is on */\ + prev_prime = apply(prime,prev_prime,DISJOIN,manager,limited);\ + if(prev_prime==NULL) return 0; /* limits exceeded */\ + }\ + else { /* just popped a new element */\ + PUSH_ELM(prev_prime,prev_sub,cp_stack1,manager);\ + prev_prime = prime;\ + prev_sub = sub;\ + }\ + }\ + PUSH_ELM(prev_prime,prev_sub,cp_stack1,manager);\ + assert(STACK_SIZE(cp_stack1,manager)==1 ||\ + elements_sorted_and_compressed(STACK_SIZE(cp_stack1,manager),STACK_START(cp_stack1,manager)));\ +} + +/**************************************************************************************** + * cartesian product + ****************************************************************************************/ + +//declare the beginning of a cartesian product +void open_cartesian_product(SddManager* manager) { + RESET_STACK(cp_stack1,manager); + //start with a partition on stack1 that has a single element: true.false + PUSH_ELM(manager->true_sdd,manager->false_sdd,cp_stack1,manager); +} + +//declare the end of a cartesian product and return its size and elements +int close_cartesian_product(int compress, SddNodeSize* size, SddElement** elements, Vtree* vtree, SddManager* manager, int limited) { + //stack1 already compressed (but not sorted) + if(compress) { + SWITCH_ELM_STACKS(cp_stack1,cp_stack3,manager); //uncompressed elements in stack3 now + COMPRESS_S3_to_S1(manager,limited); //compressed elements in stack1 now (may fail returning with 0) + } + *size = STACK_SIZE(cp_stack1,manager); + *elements = new_elements(*size,manager); + memcpy(*elements,STACK_START(cp_stack1,manager),*size*sizeof(SddElement)); + assert(*size > 1); + return 1; +} + +/**************************************************************************************** + * partition + ****************************************************************************************/ + +//declare the beginning of a new set in the cartesian product +void open_partition(SddManager* manager) { + RESET_STACK(cp_stack2,manager); +} + +void declare_element_of_partition(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager* manager) { + assert(!IS_FALSE(prime)); + assert(IS_TRUE(prime) || sdd_vtree_is_sub(prime->vtree,vtree->left)); + assert(TRIVIAL(sub) || sdd_vtree_is_sub(sub->vtree,vtree->right)); + PUSH_ELM(prime,sub,cp_stack2,manager); +} + +static inline +void push_element_to_stack3(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager* manager) { + //prime could be true + assert(!IS_FALSE(prime)); + assert(IS_TRUE(prime) || sdd_vtree_is_sub(prime->vtree,vtree->left)); + assert(TRIVIAL(sub) || sdd_vtree_is_sub(sub->vtree,vtree->right)); + PUSH_ELM(prime,sub,cp_stack3,manager); +} + +//declare the end of a new set in the cartesian product +//multiply the partitions in stack_1 and stack_2, possibly compress, then put result on stack_1 +int close_partition(int compress, Vtree* vtree, SddManager* manager, int limited) { + + SddElement* elements1 = STACK_START(cp_stack1,manager); + SddElement* elements2 = STACK_START(cp_stack2,manager); + SddSize size1 = STACK_SIZE(cp_stack1,manager); + SddSize size2 = STACK_SIZE(cp_stack2,manager); + + assert(size1 >= 1 && size2 >= 1); + + //multiply elements1 and elements2 and put on stack3 + RESET_STACK(cp_stack3,manager); + //timeout check embedded into multiply_decompositions + int success = + multiply_decompositions( + elements1,size1, + elements2,size2, + DISJOIN,vtree,manager,limited, + push_element_to_stack3); + + if(!success) return 0; //time limit exceeded + + if(0 && compress) { //compress stack3 and put result on stack1 (no trimming is possible) + COMPRESS_S3_to_S1(manager,limited); //may fail returning with 0 + } + else { //no compression: just switch stack1 and stack3 + SWITCH_ELM_STACKS(cp_stack1,cp_stack3,manager); + } + + //current cartesian product in stack1 + if(limited && + STACK_SIZE(cp_stack1,manager) > manager->vtree_ops.cartesian_product_limit) { + ++manager->vtree_ops.failed_count_cp; + return 0; //failure + } + else return 1; //success + +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c new file mode 100644 index 0000000..7bce240 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/dissect.c @@ -0,0 +1,78 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + + +/**************************************************************************************** + * convert vtree into a left-linear one + * return new root + ****************************************************************************************/ + +Vtree* left_linearize_vtree(Vtree* vtree, SddManager* manager) { + Vtree** root_location = sdd_vtree_location(vtree,manager); + while(INTERNAL(vtree)) { + while(INTERNAL(vtree->right)) { + sdd_vtree_rotate_left(vtree->right,manager,0); + vtree = vtree->parent; + } + vtree = vtree->left; + } + return *root_location; +} + +/**************************************************************************************** + * convert vtree into a right-linear one (obdd) + * return new root + * + * proposed by Stout and Warren + ****************************************************************************************/ + +Vtree* right_linearize_vtree(Vtree* vtree, SddManager* manager) { + Vtree** root_location = sdd_vtree_location(vtree,manager); + while(INTERNAL(vtree)) { + while(INTERNAL(vtree->left)) { + sdd_vtree_rotate_right(vtree,manager,0); + vtree = vtree->parent; + } + vtree = vtree->right; + } + return *root_location; +} + +/**************************************************************************************** + * convert vtree into a balanced one + * return new root + * + * this is known as the Stout and Warren algorithm, which improves Day's algorithm + ****************************************************************************************/ + +Vtree* balance_vtree(Vtree* vtree, SddManager* manager) { + + Vtree** root_location = sdd_vtree_location(vtree,manager); + vtree = right_linearize_vtree(vtree,manager); + + SddLiteral b = vtree->var_count -2; + SddLiteral m = b/2; + + while(m > 0) { + for(SddLiteral i=0; iright; + sdd_vtree_rotate_left(vtree,manager,0); + vtree = vtree->right; + } + b = b - m - 1; + m = b / 2; + vtree = *root_location; //go to root + } + + return *root_location; +} + + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c new file mode 100644 index 0000000..9932df6 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/limits.c @@ -0,0 +1,237 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * a limit of 0 means no limit + * + * types of limits: + * + * --time: + * limits the time spent by a function call + * applicable to vtree search, fragment search, vtree operations, and apply + * + * --size: + * limits the growth in size of an sdd normalized for a given vtree + * applicable to vtree operations + * requires one to specify the vtree whose sdd size is to be limited + * + * --memory: + * limits the growth of memory used by the manager's sdd (memory of nodes and elements) + * applicable to vtree operations + * + * vtree search calls fragment search, which calls vtree operations, which call apply; + * any of these four stages can be subjected to limits + * + * fragment search and vtree operations can also be called directly by the user + * (perhaps when implementing their own vtree search) + * user can also choose to apply limits in this case + * + ****************************************************************************************/ + +// +//limiting vtree search +// +void start_search_limits(SddManager* manager) { //called upon starting vtree search + assert(manager->vtree_ops.search_aborted==0); + manager->vtree_ops.search_time_stamp = clock(); +} +void end_search_limits(SddManager* manager) { //called upon finishing vtree search + manager->vtree_ops.search_time_stamp = 0; //apply limit only when stamp is not 0 + manager->vtree_ops.search_aborted = 0; +} +int search_aborted(SddManager* manager) { //called to check whether vtree search has been aborted + return manager->vtree_ops.search_aborted; +} + +// +//limiting fragment search +// +void start_fragment_limits(SddManager* manager) { //called upon starting fragment search + assert(manager->vtree_ops.fragment_aborted==0); + manager->vtree_ops.fragment_time_stamp = clock(); +} +void end_fragment_limits(SddManager* manager) { //called upon finishing fragement search + manager->vtree_ops.fragment_time_stamp = 0; //apply limit only when stamp is not 0 + manager->vtree_ops.fragment_aborted = 0; +} +int fragment_aborted(SddManager* manager) { //called to check whether a fragment search has been aborted + return manager->vtree_ops.fragment_aborted; +} + +// +//limiting vtree ops (left-rotate, right-rotate or swap) +// +void start_op_limits(SddManager* manager) { //called upon starting a vtree op + assert(manager->vtree_ops.op_aborted==0); + manager->vtree_ops.op_time_stamp = clock(); + manager->vtree_ops.op_memory_stamp = TYPE2MB(manager->node_count,SddNode)+TYPE2MB(manager->sdd_size,SddElement); +} +void end_op_limits(SddManager* manager) { //called up finishing a vtree op + manager->vtree_ops.op_time_stamp = 0; //apply limit only when stamp is not 0 + manager->vtree_ops.op_memory_stamp = 0; //apply limit only when stamp is not 0 + manager->vtree_ops.op_aborted = 0; +} +int op_aborted(SddManager* manager) { //not used + return manager->vtree_ops.op_aborted; +} + +// +//limiting apply +// +void start_apply_limits(SddManager* manager) { //called upon starting a root, limited apply + assert(manager->vtree_ops.apply_aborted==0); + manager->vtree_ops.apply_time_stamp = clock(); +} +void end_apply_limits(SddManager* manager) { //called upon finishing a root, limited apply + manager->vtree_ops.apply_time_stamp = 0; //apply limit only when stamp is not 0 + manager->vtree_ops.apply_aborted = 0; +} +int apply_aborted(SddManager* manager) { //called only in assertions + return manager->vtree_ops.apply_aborted; +} + +/**************************************************************************************** + * time and memory limits: checked only within a limited apply, + * which is called only by a limited vtree op + * + * memory limits are only applicable to vtree ops + ****************************************************************************************/ + +static inline +int exceeded_op_memory_limit(SddManager* manager) { + + float memory_limit = manager->vtree_ops.op_memory_limit; + if(memory_limit==0) return 0; + + float init_memory = VTREE_OP_MEMORY_MIN+manager->vtree_ops.op_memory_stamp; + float cur_memory = TYPE2MB(manager->node_count,SddNode)+TYPE2MB(manager->sdd_size,SddElement); + int memory_exceeded = cur_memory > init_memory*memory_limit; + + if(memory_exceeded) { + switch(manager->vtree_ops.current_op) { + case 'l': ++manager->vtree_ops.failed_lr_count_memory; break; + case 'r': ++manager->vtree_ops.failed_rr_count_memory; break; + case 's': ++manager->vtree_ops.failed_sw_count_memory; break; + } + } + + return memory_exceeded; +} + +//--first checks memory limit for vtree op, then checks time limits in this order (when applicable): +// search, fragment, op, apply +//--records first reason for exceeding limits (if any) + +int exceeded_limits(SddManager* manager) { + + //limits are checked only in a limited apply, which is called only by a (limited) vtree op + assert(manager->vtree_ops.current_op != ' '); //we must be in the context of a vtree op + + //clock() is expensive, so check limits only after that many applies + if(manager->stats.apply_count%LIMITS_CHECK_FREQUENCY) return 0; + + //check should not be made again if already succeeded (that is, some limit has been exceeded) + assert(manager->vtree_ops.search_aborted==0); + assert(manager->vtree_ops.fragment_aborted==0); + assert(manager->vtree_ops.op_aborted==0); + assert(manager->vtree_ops.apply_aborted==0); + + //check memory limit first + if(exceeded_op_memory_limit(manager)) return manager->vtree_ops.op_aborted = 1; + + //check time limits next + clock_t cur_time = clock(); //equality may hold in assertions below + assert(cur_time >= manager->vtree_ops.search_time_stamp); + assert(cur_time >= manager->vtree_ops.fragment_time_stamp); + assert(cur_time >= manager->vtree_ops.op_time_stamp); + assert(cur_time >= manager->vtree_ops.apply_time_stamp); + + if(manager->vtree_ops.search_time_limit && manager->vtree_ops.search_time_stamp && + cur_time > manager->vtree_ops.search_time_limit+manager->vtree_ops.search_time_stamp) { + ++manager->auto_search_invocation_count_aborted_search; + manager->vtree_ops.search_aborted = 1; + } + else if(manager->vtree_ops.fragment_time_limit && manager->vtree_ops.fragment_time_stamp && + cur_time > manager->vtree_ops.fragment_time_limit+manager->vtree_ops.fragment_time_stamp) { + ++manager->auto_search_invocation_count_aborted_fragment; + manager->vtree_ops.fragment_aborted = 1; + } + else if(manager->vtree_ops.op_time_limit && manager->vtree_ops.op_time_stamp && + cur_time > manager->vtree_ops.op_time_limit+manager->vtree_ops.op_time_stamp) { + ++manager->auto_search_invocation_count_aborted_operation; + manager->vtree_ops.op_aborted = 1; + } + else if(manager->vtree_ops.apply_time_limit && manager->vtree_ops.apply_time_stamp && + cur_time > manager->vtree_ops.apply_time_limit+manager->vtree_ops.apply_time_stamp) { + ++manager->auto_search_invocation_count_aborted_apply; + manager->vtree_ops.apply_aborted = 1; + } + else return 0; + + switch(manager->vtree_ops.current_op) { + case 'l': ++manager->vtree_ops.failed_lr_count_time; break; + case 'r': ++manager->vtree_ops.failed_rr_count_time; break; + case 's': ++manager->vtree_ops.failed_sw_count_time; break; + } + + return 1; //time out +} + +/**************************************************************************************** + * size limit: checked only within a vtree op (left-rotate, right-rotate & swap) + * + * to use this limit, one must first call sdd_manager_init_vtree_size_limit() to set + * the reference size for imposing the size limit. this reference size can be updated + * efficiently using a call to sdd_manager_update_vtree_size_limit() + ****************************************************************************************/ + +//declares the baseline size for enforcing a size limit: the current size of vtree +void sdd_manager_init_vtree_size_limit(Vtree* vtree, SddManager* manager) { + manager->vtree_ops.op_size_stamp = sdd_vtree_live_size(vtree); + //outside size is constant and needed for efficiently computing current size + manager->vtree_ops.outside_size = sdd_manager_live_size(manager)-manager->vtree_ops.op_size_stamp; +} + +//updates the baseline size for enforcing size limits +//avoid recomputing the size of the vtree whose size is being limited +void sdd_manager_update_vtree_size_limit(SddManager* manager) { + manager->vtree_ops.op_size_stamp = sdd_manager_live_size(manager)-manager->vtree_ops.outside_size; +} + +//--this function is called after each node has been processed by a vtree operation +// +//--nodes to be processed by vtree operations are outside the unique table and, hence, +// their sizes are not accounted for in sizes maintained by the manager and vtree nodes +//--offset_size represent the current size of such nodes: it must be added to the +// current manager size to get the correct size + +int exceeded_size_limit(SddSize offset_size, SddManager* manager) { + + float size_limit = manager->vtree_ops.op_size_limit; + if(size_limit==0) return 0; + + SddSize cur_size = (offset_size+sdd_manager_live_size(manager))-manager->vtree_ops.outside_size; + if(cur_size <= VTREE_OP_SIZE_MIN) return 0; //don't enforce limit for small enough sizes + + SddSize init_size = manager->vtree_ops.op_size_stamp; + int size_exceeded = cur_size > size_limit*init_size; + + if(size_exceeded) { + switch(manager->vtree_ops.current_op) { + case 'l': ++manager->vtree_ops.failed_lr_count_size; break; + case 'r': ++manager->vtree_ops.failed_rr_count_size; break; + case 's': ++manager->vtree_ops.failed_sw_count_size; break; + } + } + + return size_exceeded; +} + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c new file mode 100644 index 0000000..634dc46 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_left_rotate.c @@ -0,0 +1,143 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" +#include "vtree_ops.h" + +//local declarations +static int try_rotating_partition_left(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* x, SddManager* manager, int limited); + +/**************************************************************************************** + * left rotating a vtree node and its associated sdd nodes + ****************************************************************************************/ + +//w = (a x=(b c)) ===left rotation of x===> x = (w=(a b) c) +// +//returns 1 if rotation is done within limits, otherwise returns 0 +//0 means no limit +int sdd_vtree_rotate_left(Vtree* x, SddManager* manager, int limited) { + + if(limited) start_op_limits(manager); + + //stats + manager->vtree_ops.current_op = 'l'; + manager->vtree_ops.current_vtree = x->position; + ++manager->vtree_ops.lr_count; + + Vtree* w = x->parent; + assert(!FULL_DEBUG || verify_gc(w,manager)); + + //unique nodes at vtree x continue to be normalized for x after left rotating x + //unique nodes at w fall into three groups: + //--w(a,bc): must be rotated and moved to x (this is the bc_list) + //--w(a,c) : must be moved to x (this is the c_list) + //--w(a,b) : stays at w (this is the ab_list) + //rotation may create or lookup nodes at w, so the ab_list must be at w during rotation + +// SddSize init_count = manager->node_count; + SddSize init_size = sdd_manager_live_size(manager); + SddSize bc_count; SddNode* bc_list; SddNode* c_list; + split_nodes_for_left_rotate(&bc_count,&bc_list,&c_list,w,x,manager); //must be done before rotating vtree + + //rotate vtree structure + rotate_vtree_left(x,manager); + Vtree* new_root = x; + + //rotate sdd nodes +// SddSize offset_count = init_count-manager->node_count; //count of nodes currently outside the unique table (all live) + SddSize offset_size = init_size-sdd_manager_live_size(manager); //size of nodes currently outside the unique table (all live) + SddNodeSize new_size; //size of rotated elements + SddElement* new_elements; //rotated elements + int success = 1; + + FOR_each_linked_node(n,bc_list,{ + WITH_no_auto_mode(manager, + success=try_rotating_partition_left(&new_size,&new_elements,n,x,manager,limited)); + if(success) { //node gets new elements and size + offset_size -= n->size; //in two steps to avoid underflow + offset_size += new_size; + replace_node(1,n,new_size,new_elements,x,manager); //reversible + } + if(!success || (limited && exceeded_size_limit(offset_size,manager))) { //rollback + rotate_vtree_right(x,manager); //reverse vtree edit + //recover elements and move bc_list back to w + //move c_list back to w + rollback_vtree_op(bc_list,c_list,w,manager); + new_root = w; //back to original root + success = 0; //rotate may have succeeded, but limit exceeded + goto done; + } + + }); + + assert(success); + //confirm successful rotation of bc_list and moved to x + //move c_list to x + finalize_vtree_op(bc_list,c_list,x,manager); + + done: + + //when sdd_vtree_rotate_left() is called, it assumes no dead nodes above vtree w, + //and will not create dead nodes above new_root; hence, we only need to gc in new_root + garbage_collect_in(new_root,manager); + assert(!FULL_DEBUG || verify_gc(new_root,manager)); + assert(!FULL_DEBUG || verify_counts_and_sizes(manager)); + manager->vtree_ops.current_op = ' '; + + if(limited) end_op_limits(manager); + return success; +} + +/**************************************************************************************** + * left-rotates the partition of an sdd node + * + * if successful, the rotated partition is returned (size, elements) + ****************************************************************************************/ + +//w = (a x=(b c)) ===left rotation of x===> x = (w=(a b) c) +//node depends on a, b and c +//node normalized for vtree w (before rotation) +//this function is called after left rotation of vtree x +//returns 1 if rotation succeeds (done within time limit), 0 otherwise +static +int try_rotating_partition_left(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* x, SddManager* manager, int limited) { + Vtree* w = x->left; + + START_partition(manager); + + FOR_each_prime_sub_of_node(a,bc,node,{ + //bc can be true, false, a literal or a decomposition + if(TRIVIAL(bc)) DECLARE_element(a,bc,x,manager); + else if(bc->vtree==x) { //bc is a decomposition, normalized for vtree x + FOR_each_prime_sub_of_node(b,c,bc,{ + //prime ab cannot be false since neither a nor b can be false + SddNode* ab = sdd_conjoin_lr(a,b,w,manager); //not limited + assert(ab!=NULL); + DECLARE_element(ab,c,x,manager); + }); + } + //bc is literal or decomposition, normalized for a vtree in b or a vtree in c + else if(bc->vtree->position > x->position) { //bc is normalized for a vtree in c + //view bc as a decomposition true.bc normalized for vtree x + DECLARE_element(a,bc,x,manager); + } + else { //bc is normalized for a vtree in b + //view bc as a decomposition (bc.true + ~bc.false) normalized for vtree x + SddNode* ab = sdd_conjoin_lr(a,bc,w,manager); //not limited + DECLARE_element(ab,manager->true_sdd,x,manager); + SddNode* bc_neg = sdd_negate(bc,manager); + ab = sdd_conjoin_lr(a,bc_neg,w,manager); //not limited + assert(ab!=NULL); + DECLARE_element(ab,manager->false_sdd,x,manager); + } + }); + + return GET_elements_of_partition(size,elements,x,manager,limited); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c new file mode 100644 index 0000000..2a8183e --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_right_rotate.c @@ -0,0 +1,150 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" +#include "vtree_ops.h" + +//local declarations +static int try_rotating_partition_right(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* w, SddManager* manager, int limited); + +/**************************************************************************************** + * right rotating a vtree node and its associated sdd nodes + ****************************************************************************************/ + +//x = (w=(a b) c) ===right rotation of x===> w = (a x=(b c)) +// +//returns 1 if rotation is done within limits, otherwise returns 0 +//0 means no limit +int sdd_vtree_rotate_right(Vtree* x, SddManager* manager, int limited) { + + if(limited) start_op_limits(manager); + + //stats + manager->vtree_ops.current_op = 'r'; + manager->vtree_ops.current_vtree = x->position; + ++manager->vtree_ops.rr_count; + + Vtree* w = x->left; + assert(!FULL_DEBUG || verify_gc(x,manager)); + + //unique nodes at vtree w continue to be normalized for w after right rotating x + //unique nodes at x fall into three groups: + //--x(ab,c): must be rotated and moved to w (this is the ab_list) + //--x(a,c) : must be moved to w (this is the a_list) + //--x(b,c) : stays at x (this is the bc_list) + //rotation may create or lookup nodes at x, so the bc_list must be at x during rotation + +// SddSize init_count = manager->node_count; + SddSize init_size = sdd_manager_live_size(manager); + SddSize ab_count; SddNode* ab_list; SddNode* a_list; + split_nodes_for_right_rotate(&ab_count,&ab_list,&a_list,x,w,manager); //must be done before rotating vtree + + //rotate vtree structure + rotate_vtree_right(x,manager); + Vtree* new_root = w; + + //rotate sdd nodes +// SddSize offset_count = init_count-manager->node_count; //count of nodes currently outside the unique table (all live) + SddSize offset_size = init_size-sdd_manager_live_size(manager); //size of nodes currently outside the unique table (all live) + SddNodeSize new_size; //size of rotated elements + SddElement* new_elements; //rotated elements + int success = 1; + + FOR_each_linked_node(n,ab_list,{ + WITH_no_auto_mode(manager, + success=try_rotating_partition_right(&new_size,&new_elements,n,w,manager,limited)); + if(success) { //node gets new elements and size + offset_size -= n->size; //in two steps to avoid underflow + offset_size += new_size; + replace_node(1,n,new_size,new_elements,w,manager); //reversible + } + if(!success || (limited && exceeded_size_limit(offset_size,manager))) { //rollback + rotate_vtree_left(x,manager); //reverse vtree edit + //recover elements and move ab_list back to x + //move a_list back to x + rollback_vtree_op(ab_list,a_list,x,manager); + new_root = x; //back to original root + success = 0; //rotate may have succeeded, but limit exceeded + goto done; + } + + }); + + assert(success); + //confirm successful rotation of ab_list and moved to w + //move a_list to w + finalize_vtree_op(ab_list,a_list,w,manager); + + done: + + //when sdd_vtree_rotate_right() is called, it assumes no dead nodes above vtree x, + //and will not create dead nodes above new_root; hence, we only need to gc in new_root + garbage_collect_in(new_root,manager); + assert(!FULL_DEBUG || verify_gc(new_root,manager)); + assert(!FULL_DEBUG || verify_counts_and_sizes(manager)); + manager->vtree_ops.current_op = ' '; + + if(limited) end_op_limits(manager); + return success; +} + +/**************************************************************************************** + * right-rotates the partition of an sdd node + * + * if successful, the rotated partition is returned (size, elements) + ****************************************************************************************/ + +//x = (w=(a b) c) ===right rotation of x===> w = (a x=(b c)) +//nodes depends on a b and c +//node normalized for vtree x (before rotation) +//this function is called after right rotation of vtree x +//returns 1 if rotation succeeds (done within limits), 0 otherwise +static +int try_rotating_partition_right(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* w, SddManager* manager, int limited) { + Vtree* x = w->right; + + open_cartesian_product(manager); + + FOR_each_prime_sub_of_node(ab,c,node,{ + //ab is either a literal or a decomposition + //ab cannot be trivial + open_partition(manager); + + if(ab->vtree==w) { //ab is a decomposition, normalized for vtree w + FOR_each_prime_sub_of_node(a,b,ab,{ + SddNode* bc = sdd_conjoin_lr(b,c,x,manager); //not limited + assert(bc!=NULL); + declare_element_of_partition(a,bc,w,manager); + }); + } + //ab is normalized for a vtree in a or a vtree in b + else if(sdd_vtree_is_sub(ab->vtree,w->right)) { //ab is normalized for a vtree in b + //view ab as a decomposition true.ab normalized for vtree w + SddNode* a = manager->true_sdd; + SddNode* bc = sdd_conjoin_lr(ab,c,x,manager); //not limited + assert(bc!=NULL); + declare_element_of_partition(a,bc,w,manager); + } + else { //ab is normalized for a vtree in a + //view ab as a decomposition (ab.true + ~ab.false) normalized for vtree w + SddNode* a = ab; + SddNode* bc = c; + declare_element_of_partition(a,bc,w,manager); + a = sdd_negate(ab,manager); + bc = manager->false_sdd; + declare_element_of_partition(a,bc,w,manager); + } + + if(!close_partition(1,w,manager,limited)) return 0; //with compression + }); + + return close_cartesian_product(1,size,elements,w,manager,limited); //with compression +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c new file mode 100644 index 0000000..c015d1d --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/op_swap.c @@ -0,0 +1,112 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" +#include "vtree_ops.h" + +//local declarations +static int try_swapping_partition(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* vtree, SddManager* manager, int limited); + +/**************************************************************************************** + * swapping a vtree node and all its associated sdd nodes + ****************************************************************************************/ + +//v = (a b) ===swap v===> v = (b a) +// +//return 1 if swapping is done within limits, otherwise return 0 +//0 means no limit +int sdd_vtree_swap(Vtree* v, SddManager* manager, int limited) { + + if(limited) start_op_limits(manager); + + //stats + manager->vtree_ops.current_op = 's'; + manager->vtree_ops.current_vtree = v->position; + ++manager->vtree_ops.sw_count; + + assert(!FULL_DEBUG || verify_gc(v,manager)); + + //remove nodes of v from unique table and collect them in a linked list; + SddSize init_size = sdd_manager_live_size(manager); + SddSize count = v->node_count; //before splitting + SddNode* n_list = split_nodes_for_swap(v,manager); + //swapped nodes are removed and reinserted into hash table since their + //hash keys may changed due to swapping (hash keys depend on primes/subs) + + //swap vtree structure + swap_vtree_children(v,manager); + if(count==0) return 1; //optimization: no nodes to swap + + //swap sdd nodes + SddSize offset_size = init_size-sdd_manager_live_size(manager); //size of nodes currently outside the unique table (all live) + SddNodeSize new_size; //size of swapped elements + SddElement* new_elements; //swapped elements + int success = 1; + + FOR_each_linked_node(n,n_list,{ + WITH_no_auto_mode(manager, + success=try_swapping_partition(&new_size,&new_elements,n,v,manager,limited)); + if(success) { //node gets new elements and size + offset_size -= n->size; //in two steps to avoid underflow + offset_size += new_size; + replace_node(1,n,new_size,new_elements,v,manager); //reversible + } + if(!success || (limited && exceeded_size_limit(offset_size,manager))) { //rollback + swap_vtree_children(v,manager); //reverse vtree edit + rollback_vtree_op(n_list,NULL,v,manager); //recover elements and move n_list back to v + success = 0; //swap may have succeeded, but limit exceeded + goto done; + } + + }); + + assert(success); + //confirm successful swapping of n_list and move back into v + finalize_vtree_op(n_list,NULL,v,manager); + + done: + + //when sdd_vtree_swap() is called, it assumes no dead nodes above vtree v, and + //will not create dead nodes above v; hence, we only need to gc in v + garbage_collect_in(v,manager); + assert(!FULL_DEBUG || verify_gc(v,manager)); + assert(!FULL_DEBUG || verify_counts_and_sizes(manager)); + manager->vtree_ops.current_op = ' '; + + if(limited) end_op_limits(manager); + return success; +} + +/**************************************************************************************** + * swaps the partition of an sdd node + * + * if successful, the swapped partition is returned (size, elements) + ****************************************************************************************/ + +//v = (a b) ===swap===> v = (b a) +//node normalized for v +//this function is called after vtree v has been swapped +//returns 1 if swapping succeeds (done within limits), 0 otherwise +int try_swapping_partition(SddNodeSize* size, SddElement** elements, SddNode* node, Vtree* v, SddManager* manager, int limited) { + + open_cartesian_product(manager); + + FOR_each_prime_sub_of_node(a,b,node,{ + open_partition(manager); + + SddNode* neg_b = sdd_negate(b,manager); + if(!IS_FALSE(b)) declare_element_of_partition(b,a,v,manager); + if(!IS_FALSE(neg_b)) declare_element_of_partition(neg_b,manager->false_sdd,v,manager); + + if(!close_partition(0,v,manager,limited)) return 0; //with no compression + }); + + return close_cartesian_product(0,size,elements,v,manager,limited); //with no compression +} + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c new file mode 100644 index 0000000..faabb3a --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/rollback.c @@ -0,0 +1,68 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/nodes.c +void insert_in_unique_table(SddNode* node, SddManager* manager); +void inc_element_parent_counts(SddSize size, SddElement* elements, SddManager* manager); +void dec_element_parent_counts(SddSize size, SddElement* elements, SddManager* manager); + +//basic/replace.c +void reverse_node_replacement(SddNode* node, Vtree* vtree, SddManager* manager); +void confirm_node_replacement(SddNode* node, SddManager* manager); + +/**************************************************************************************** + * moves nodes to a new vtree + ****************************************************************************************/ + +//replace the vtree of nodes +//insert nodes in unique table +//nodes is a linked list +static inline +void move_to_vtree(SddNode* nodes, Vtree* vtree, SddManager* manager) { + FOR_each_linked_node(n,nodes,{ //move to nodes of vtree + n->vtree = vtree; + insert_in_unique_table(n,manager); + }); +} + +/**************************************************************************************** + * confirm nodes replacement as rollback is not longer needed + ****************************************************************************************/ + +//free saved elements of replaced_nodes +//insert replaced_nodes into unique table (nodes already normalized for vtree) +//move moved_nodes to vtree +//replaced_nodes and moved_nodes are a linked list +void finalize_vtree_op(SddNode* replaced_nodes, SddNode* moved_nodes, Vtree* vtree, SddManager* manager) { + FOR_each_linked_node(n,replaced_nodes,{ + assert(n->replaced); + confirm_node_replacement(n,manager); //frees saved elements + insert_in_unique_table(n,manager); + }); + move_to_vtree(moved_nodes,vtree,manager); +} + +/**************************************************************************************** + * undo node replacement due a rollback + ****************************************************************************************/ + +//reverse the replacement of replaced_nodes (if any) +//insert replaced_nodes back into unique table +//move moved_nodes to vtree +//replaced_nodes and moved_nodes are a linked list +void rollback_vtree_op(SddNode* replaced_nodes, SddNode* moved_nodes, Vtree* vtree, SddManager* manager) { + FOR_each_linked_node(n,replaced_nodes,{ + if(n->replaced) reverse_node_replacement(n,vtree,manager); //not all nodes may have been replaced + insert_in_unique_table(n,manager); + }); + move_to_vtree(moved_nodes,vtree,manager); +} + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/split.c b/pysdd/lib/libsdd-2.0/src/vtree_operations/split.c new file mode 100644 index 0000000..99af822 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/split.c @@ -0,0 +1,180 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//basic/nodes.c +void remove_from_unique_table(SddNode* node, SddManager* manager); + +/**************************************************************************************** + * when rotating or swapping sdd nodes that are normalized for a given vtree, we need + * to split these nodes into groups as each group will need to be processed differently. + ****************************************************************************************/ + +/**************************************************************************************** + * splitting sdd nodes normalized for vtree w = (a x=(b c)) + * (in preparation for LEFT rotating x) + * + * split sdd nodes normalized for w as follows: + * --nodes that depend on b and c are removed from unique table and returned in bc_list + * --nodes that depend on c only are removed from unique table and returned in c_list + * --nodes that depend on b only stay in the unique table + ****************************************************************************************/ + +//node is normalized for w = (a x=(b c)) +//hence: +//--node must depend on a (i.e., on some variable in vtree a) +//--node must depend on either b or c as well +// +//return +//'b' if node depends on b only +//'c' if node depends on c only +//'^" if node depends on both b and c +static inline +char dependence_on_right_vtree(SddNode* node, Vtree* x) { + + int depends_on_b = 0; + int depends_on_c = 0; + SddLiteral x_p = x->position; + + FOR_each_sub_of_node(sub,node,{ + //sub can be true, false, literal or decomposition + if(NON_TRIVIAL(sub)) { + SddLiteral sub_p = sub->vtree->position; + if(sub_p==x_p) return '^'; + else if(sub_p < x_p) depends_on_b = 1; + else depends_on_c = 1; //sub_p > x_p + if(depends_on_b && depends_on_c) return '^'; + } + }); + + assert(depends_on_b || depends_on_c); + assert(!(depends_on_b && depends_on_c)); + + if(depends_on_b) return 'b'; + else return 'c'; + +} + +//w = (a x=(b c)) +//in preparation of left rotating x: +// --split nodes into bc_list, c_list and rest +// --remove nodes on bc_list and c_list from unique table, while keeping rest in the table +// --return bc_list, c_list and size of bc_list +void split_nodes_for_left_rotate(SddSize* bc_count, SddNode** bc_list, SddNode** c_list, Vtree* w, Vtree* x, SddManager* manager) { + + *bc_count = 0; *bc_list = *c_list = NULL; + + FOR_each_sdd_node_normalized_for(n,w,{ + char type = dependence_on_right_vtree(n,x); + if(type!='b') { //do not remove nodes of type 'b' + remove_from_unique_table(n,manager); //first + //->next field is used to index n into unique table (not needed any more) + if(type=='^') { ++(*bc_count); n->next = *bc_list; *bc_list = n; } + else { assert(type=='c'); n->next = *c_list; *c_list = n; } + } + }); + + sort_linked_nodes(*bc_count,bc_list,manager); + //this seems to be more efficient as it avoids premature violation of size limits +} + +/**************************************************************************************** + * splitting sdd nodes normalized for vtree x = (w=(a b) c) + * (in preparation of RIGHT rotating x) + * + * split sdd nodes normalized for x as follows: + * --nodes that depend on a and b are removed from unique table and returned in ab_list + * --nodes that depend on a only are removed from unique table and returned in a_list + * --nodes that depend on b only stay in the unique table + ****************************************************************************************/ + +//node is normalized for x = (w=(a b) c) +//hence: +//--node must depend on c (i.e., must depend on some variable in vtree c) +//--node must depend on either a or b +// +//return +//'a' if node depends only on a +//'b' if node depends only on b +//'^' if node depends on both a and b +// +static inline +char dependence_on_left_vtree(SddNode* node, Vtree* w) { + + int depends_on_a = 0; + int depends_on_b = 0; + SddLiteral w_p = w->position; + + FOR_each_prime_of_node(prime,node,{ + assert(NON_TRIVIAL(prime)); + //prime could be literal or decomposition (cannot be true or false) + SddLiteral prime_p = prime->vtree->position; + if(prime_p == w_p) return '^'; + else if(prime_p < w_p) depends_on_a = 1; + else depends_on_b = 1; //prime_p > w_p + if(depends_on_a && depends_on_b) return '^'; + }); + + assert(depends_on_a || depends_on_b); + assert(!(depends_on_a && depends_on_b)); + + if(depends_on_a) return 'a'; + else return 'b'; + +} + +//x = (w=(a b) c) +//in preparation of right rotating x: +// --split nodes into ab_list, a_list and rest +// --remove nodes on ab_list and a_list from the unique table, while keeping rest in the able +// --return ab_list, a_lists and size of ab_list +void split_nodes_for_right_rotate(SddSize *ab_count, SddNode** ab_list, SddNode** a_list, Vtree* x, Vtree* w, SddManager* manager) { + + *ab_count = 0; *ab_list = *a_list = NULL; + + FOR_each_sdd_node_normalized_for(n,x,{ + char type = dependence_on_left_vtree(n,w); + if(type!='b') { //do not remove nodes of type 'b' + remove_from_unique_table(n,manager); //first + //->next field is used to index n into unique table (not needed any more) + if(type=='^') { ++(*ab_count); n->next = *ab_list; *ab_list = n; } + else { assert(type=='a'); n->next = *a_list; *a_list = n; } + } + }); + + sort_linked_nodes(*ab_count,ab_list,manager); + //this seems to be more efficient as it avoids premature violation of size limits +} + +/**************************************************************************************** + * splitting sdd nodes for normalized for v + * (in preparation of swapping v) + ****************************************************************************************/ + +//remove nodes of vtree from unique table and collect them in a linked list +SddNode* split_nodes_for_swap(Vtree* v, SddManager* manager) { + + SddSize count = v->node_count; + SddNode* list = NULL; + + FOR_each_sdd_node_normalized_for(n,v,{ + remove_from_unique_table(n,manager); //first + //->next field is used to index n into unique table (not needed any more) + n->next = list; + list = n; + }); + //list is now head of a linked list containing sdd nodes of vtree + + sort_linked_nodes(count,&list,manager); + //this seems to be more efficient as it avoids premature violation of size limits + + return list; +} + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_operations/vtree_ops.h b/pysdd/lib/libsdd-2.0/src/vtree_operations/vtree_ops.h new file mode 100644 index 0000000..a28af3e --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_operations/vtree_ops.h @@ -0,0 +1,48 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +/**************************************************************************************** + * used by rotate and swap + ****************************************************************************************/ + +//basic/gc.c +void garbage_collect_in(Vtree* vtree, SddManager* manager); + +//basic/partitions.c +void START_partition(SddManager* manager); +void DECLARE_element(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager *manager); +int GET_elements_of_partition(SddNodeSize* size, SddElement** elements, Vtree* vtree, SddManager* manager, int limited); + +//basic/replace.c +void replace_node(int reversible, SddNode* node, SddNodeSize new_size, SddElement* new_elements, Vtree* vtree, SddManager* manager); + +//sdds/apply.c +SddNode* sdd_conjoin_lr(SddNode* node1, SddNode* node2, Vtree* lca, SddManager* manager); + +//vtree_operations/cartesian_product.c +void open_cartesian_product(SddManager* manager); +int close_cartesian_product(int compress, SddNodeSize* size, SddElement** elements, Vtree* vtree, SddManager* manager, int limited); +void open_partition(SddManager* manager); +void declare_element_of_partition(SddNode* prime, SddNode* sub, Vtree* vtree, SddManager* manager); +int close_partition(int compress, Vtree* vtree, SddManager* manager, int limited); + +//vtree_operations/limits.c +void start_op_limits(SddManager* manager); +void end_op_limits(SddManager* manager); +int exceeded_size_limit(SddSize offset_size, SddManager* manager); + +//vtree_operations/rollback.c +void finalize_vtree_op(SddNode* replaced_nodes, SddNode* moved_nodes, Vtree* vtree, SddManager* manager); +void rollback_vtree_op(SddNode* replaced_nodes, SddNode* moved_nodes, Vtree* vtree, SddManager* manager); + +//vtree_operations/split.c +void split_nodes_for_left_rotate(SddSize* bc_count, SddNode** bc_list, SddNode** c_list, Vtree* w, Vtree* x, SddManager* manager); +void split_nodes_for_right_rotate(SddSize *ab_count, SddNode** ab_list, SddNode** a_list, Vtree* x, Vtree* w, SddManager* manager); +SddNode* split_nodes_for_swap(Vtree* vtree, SddManager* manager); + +/**************************************************************************************** + * END + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_search/auto.c b/pysdd/lib/libsdd-2.0/src/vtree_search/auto.c new file mode 100644 index 0000000..154d2a8 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_search/auto.c @@ -0,0 +1,186 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//sdds/apply.c +int root_apply(SddManager* manager); + +//local +static int try_auto_minimize_top(Vtree* vtree, SddManager* manager); +static int try_auto_minimize_recursive(Vtree* vtree, SddManager* manager); +static void save_size(Vtree* vtree); + +/**************************************************************************************** + * invoking vtree search and/or gc in auto mode + * + * only vtree will be changed in case this came from a recursive apply call; otherwise, + * the whole vtree may change + * + * deciding an auto trigger is all about identifying the right: + * --FEATURES to consider in designing an auto test; and + * --the corresponding THRESHOLDS for these features + ****************************************************************************************/ + +/**************************************************************************************** + * parameters + ****************************************************************************************/ + +#define GLOBAL_GROWTH 2 +#define LOCAL_GROWTH 1.15 + +//apply growth >= recursive growth, otherwise it is ineffective +#define APPLY_GROWTH 2 +#define RECURSIVE_GROWTH 2 + +//for triggering gc after any apply +#define DEAD_APPLY_GROWTH .5 + +/**************************************************************************************** + * search + ****************************************************************************************/ + +void try_auto_gc_and_minimize(Vtree* vtree, SddManager* manager) { + assert(manager->auto_gc_and_search_on); + + int top_level_apply = root_apply(manager); + int searched = top_level_apply? try_auto_minimize_top(vtree,manager): + try_auto_minimize_recursive(vtree,manager); + + //search invokes gc + if(top_level_apply && !searched) { //try gc + SddSize dead = sdd_manager_dead_count(manager)-manager->auto_apply_outside_dead_count; //apply vtree + SddSize live = sdd_manager_live_count(manager)-manager->auto_apply_outside_live_count; //apply vtree + SddSize all = dead+live; + if(dead > all*DEAD_APPLY_GROWTH) { + ++manager->auto_gc_invocation_count; + sdd_vtree_garbage_collect(vtree,manager); //local only + } + } +} + +static +Vtree* search(Vtree* vtree, SddManager* manager) { + clock_t search_time = 0; + WITH_timing(search_time,{ + if(manager->vtree_search_function!=NULL) vtree = (*((SddVtreeSearchFunc*)manager->vtree_search_function))(vtree,manager); + else vtree = sdd_vtree_minimize_limited(vtree,manager); + }); + manager->stats.auto_search_time += search_time; + manager->stats.auto_max_search_time = MAX(search_time,manager->stats.auto_max_search_time); + return vtree; +} + +/**************************************************************************************** + * searching after a top-level apply call has finished + * + * may change the whole vtree + ****************************************************************************************/ + +//called from a top-level apply +//vtree is the lca of the apply arguments +static +int try_auto_minimize_top(Vtree* vtree, SddManager* manager) { + assert(root_apply(manager)); //top-level apply + + //sizes of manager vtree + SddSize cur_size = sdd_manager_live_size(manager); //now + SddSize last_size = manager->vtree->auto_last_search_live_size; //since last search + + if(cur_size < last_size) return 0; + + //sizes of apply vtree + SddSize out_apply_size = manager->auto_apply_outside_live_size; //size outside apply vtree + SddSize cur_apply_size = sdd_manager_live_size(manager)-out_apply_size; //size of apply vtree, now + SddSize last_apply_size = vtree->auto_last_search_live_size; //size of apply vtree, after last search + + int global = !out_apply_size && cur_size >= GLOBAL_GROWTH*last_size; //manager vtree grew enough + int local = out_apply_size && cur_apply_size >= LOCAL_GROWTH*last_apply_size; //apply vtree grew enough + + Vtree* root = (out_apply_size && manager->auto_local_gc_and_search_on==0)? manager->vtree: vtree; + + if(global || local) { + ++manager->auto_search_invocation_count; + if(out_apply_size) ++manager->auto_search_invocation_count_global; + else ++manager->auto_search_invocation_count_local; + root = search(root,manager); //root may have changed + save_size(root); //establish baseline for next search + return 1; + } + else return 0; +} + +/**************************************************************************************** + * searching after a recursive apply call has finished + * + * will only change the vtree of recursive apply call + ****************************************************************************************/ + +//called from a recursive apply +static +int try_auto_minimize_recursive(Vtree* vtree, SddManager* manager) { + assert(!root_apply(manager)); //recursive apply + + //apply vtree + Vtree* apply_vtree = manager->auto_apply_vtree; + SddSize cur_apply_size = sdd_manager_live_size(manager)-manager->auto_apply_outside_live_size; //now + SddSize last_apply_size = apply_vtree->auto_last_search_live_size; //since last search inside top-level apply + + if(cur_apply_size < APPLY_GROWTH*last_apply_size) return 0; //apply vtree did not grow enough + + //this vtree + SddSize cur_vtree_size = sdd_vtree_live_size(vtree); +// SddSize last_vtree_size = vtree->auto_last_search_live_size; + int recursive = cur_vtree_size==0 || //balancing + cur_vtree_size >= RECURSIVE_GROWTH*last_apply_size; //vtree grew enough + + if(recursive) { + //automatic vtree search + ++manager->auto_search_invocation_count; + ++manager->auto_search_invocation_count_recursive; + vtree = search(vtree,manager); //only this vtree + save_size(vtree); //establish baseline for next search + return 1; + } + else return 0; +} + +/**************************************************************************************** + * saving sizes after search + ****************************************************************************************/ + +static +SddSize save_size_down(Vtree* vtree) { + if(LEAF(vtree)) return 0; + else { + return vtree->auto_last_search_live_size = + sdd_vtree_live_size_at(vtree)+ + save_size_down(vtree->left)+ + save_size_down(vtree->right); + } +} + +static +void save_size_up(Vtree* vtree) { + while(vtree) { + vtree->auto_last_search_live_size = + sdd_vtree_live_size_at(vtree)+ + vtree->left->auto_last_search_live_size+ + vtree->right->auto_last_search_live_size; + vtree = vtree->parent; + } +} + +//saves sizes of descendants of vtree, and its ancestors +static +void save_size(Vtree* vtree) { + save_size_down(vtree); + save_size_up(vtree->parent); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_search/search.c b/pysdd/lib/libsdd-2.0/src/vtree_search/search.c new file mode 100644 index 0000000..91173ab --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_search/search.c @@ -0,0 +1,332 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//vtree_operations/limits.c +void start_search_limits(SddManager* manager); +void end_search_limits(SddManager* manager); +int search_aborted(SddManager* manager); +void start_fragment_limits(SddManager* manager); +void end_fragment_limits(SddManager* manager); +int fragment_aborted(SddManager* manager); +void sdd_manager_init_vtree_size_limit(Vtree* vtree, SddManager* manager); +void sdd_manager_update_vtree_size_limit(SddManager* manager); + +//search_vtree/state.c +int is_virtual_leaf_vtree(Vtree* vtree); +Vtree* update_vtree_change(Vtree* vtree, SddManager* manager); +Vtree* update_vtree_change_p(Vtree* vtree, SddManager* manager); + +/**************************************************************************************** + * Local search algorithm for finding a good vtree: one with a minimal sdd size. + * + * See fragments/construct.c for the definition of fragments (left-linear & right-linear). + * + * The algorithm works as follows: + * + * --It makes multiple bottom-up passes through the vtree, until the reduction in the + * sdd size is small enough (see include/parameter.h). + * + * --In each pass, at internal node n, it considers both left-linear and right-linear + * fragments with node n as root (when such fragments exit). + * + * --It then considers all 12 states of each existing fragment, whenever this is feasible + * within the given time and size limits (see include/paramater.h). + * + * --It then moves the vtree to the best state found (potentially, 24 states will be + * considered). The best state is the one with the smallest sdd size (see tie breakers). + * + * The algorithm is guaranteed to improve the sdd size monotonically through its passes. + * + ***************************************************************************************/ + +/**************************************************************************************** + * quality measures + ****************************************************************************************/ + +SddSize best_size; +SddSize best_count; +SddSize best_balance; + +static inline +SddSize balance(Vtree* vtree, SddManager* manager) { + SddLiteral left_count = sdd_vtree_var_count(sdd_vtree_left(vtree)); + SddLiteral right_count = sdd_vtree_var_count(sdd_vtree_right(vtree)); + return labs(left_count - right_count); +} + +/**************************************************************************************** + * Finding the best state of a vtree fragment: potentially considering 12 states. + * + * The fragment could be left-linear r=(c=(. .) .) or right-linear r=(. c=(. .)) + ****************************************************************************************/ + +//updates index (0...11) and direction ('f' 'b') of best fragment state that beats current best +static +void best_fragment_state(int* best_state, char* best_direction, VtreeFragment* fragment, int limited) { + + SddManager* manager = fragment->manager; + + if(limited) start_fragment_limits(manager); + + char cur_direction = 'f'; //start by moving forward through states + int visited_state_count = 0; //number of fragment states examined + + //handling constrained vtrees + Vtree* X_node = NULL; + if(fragment->root->some_X_constrained_vars) { //otherwise all states are ok + Vtree* v = fragment->root->right; + while(INTERNAL(v) && v->some_X_constrained_vars) v = v->right; + if(v->some_X_constrained_vars==0) { + if(fragment->type=='l') X_node = fragment->root->right; + else if(v==fragment->child) return; //don't search this fragment + else X_node = fragment->child->right; + } + } + + while(visited_state_count < 12) { //try cycling through all 12 states + + //go to neighboring state + if(vtree_fragment_next(cur_direction,fragment,limited)) { //at neighbor + ++visited_state_count; + + //handling constrained vtrees + if(X_node && ((fragment->type=='l' && fragment->root->right != X_node) || + (fragment->type=='r' && fragment->child->right != X_node))) continue; + + //see if current state is better than best so far + Vtree* cur_root = vtree_fragment_root(fragment); + SddSize cur_size = sdd_manager_live_size(manager); + SddSize cur_count = sdd_manager_live_count(manager); + SddSize cur_balance = balance(cur_root,manager); + + int better = (cur_sizefragment_count; + if(*best_state!=-1) { //found a better state + ++manager->successful_fragment_count; + if(*best_direction=='b') ++manager->backward_successful_fragment_count; + } + + if(visited_state_count==12) { //visited all 12 states + ++manager->completed_fragment_count; + if(*best_state!=-1) ++manager->successful_completed_fragment_count; + if(cur_direction=='b') ++manager->backward_completed_fragment_count; + } + + if(limited) end_fragment_limits(manager); +} + +/**************************************************************************************** + * Determining whether left-linear and right-linear fragments exist + * + * A left-linear fragment has the form r=(c=(. .) .) + * A right-linear fragment has the form r=(. c=(. .)) + ****************************************************************************************/ + +//there is a left-linear fragment with vtree as root in case vtree->left is to be +//treated as an internal node +static inline +int is_ll_fragment(Vtree* vtree) { + return !is_virtual_leaf_vtree(vtree->left); +} + +//there is a right-linear fragment with vtree as root in case vtree->right is to be +//treated as an internal node +static inline +int is_rl_fragment(Vtree* vtree) { + return !is_virtual_leaf_vtree(vtree->right); +} + +/**************************************************************************************** + * Finding the best state of fragments rooted at vtree: potentially examining 24 state + * (12 for each fragment type) + ****************************************************************************************/ + +//returns root of best vtree found +static +Vtree* best_local_state(Vtree* root, SddManager* manager, int limited) { + + if(limited && search_aborted(manager)) return root; //search aborted + + //initialize quality measures (global variables) + best_size = sdd_manager_live_size(manager); + best_count = sdd_manager_live_count(manager); + best_balance = balance(root,manager); + + //find best state of rl fragment (if any) + VtreeFragment* fragment_rl = NULL; + int best_state_rl = -1; //none + char best_direction_rl = ' '; //undefined + + //find best state of ll fragment (if any) + VtreeFragment* fragment_ll = NULL; + int best_state_ll = -1; //none + char best_direction_ll = ' '; //undefined + + if(is_rl_fragment(root)) { + fragment_rl = vtree_fragment_new(root,root->right,manager); + best_fragment_state(&best_state_rl,&best_direction_rl,fragment_rl,limited); + if(limited && search_aborted(manager)) { + vtree_fragment_free(fragment_rl); + return root; //search aborted + } + } + + if(is_ll_fragment(root)) { + fragment_ll = vtree_fragment_new(root,root->left,manager); + best_fragment_state(&best_state_ll,&best_direction_ll,fragment_ll,limited); + if(limited && search_aborted(manager)) { + vtree_fragment_free(fragment_ll); + if(fragment_rl) vtree_fragment_free(fragment_rl); + return root; //search aborted + } + } + + //goto the best state found (following order is CRITICAL) + if(best_state_ll!=-1) { + root = vtree_fragment_goto(best_state_ll,best_direction_ll,fragment_ll); + } + else if(best_state_rl!=-1) { + root = vtree_fragment_goto(best_state_rl,best_direction_rl,fragment_rl); + } + + if(fragment_ll) vtree_fragment_free(fragment_ll); + if(fragment_rl) vtree_fragment_free(fragment_rl); + + assert(best_size==sdd_manager_live_size(manager)); + assert(best_count==sdd_manager_live_count(manager)); + assert(best_balance==balance(root,manager)); + + return root; +} + +/**************************************************************************************** + * applying a bottom-up pass of the local search algorithm + ****************************************************************************************/ + +//returns root of best vtree found +static +Vtree* local_search_pass(Vtree* vtree, SddManager* manager, int limited) { + if(is_virtual_leaf_vtree(vtree)) return vtree; + + local_search_pass(vtree->left,manager,limited); + local_search_pass(vtree->right,manager,limited); + + return best_local_state(vtree,manager,limited); +} + +/**************************************************************************************** + * local vtree search algorithm + ****************************************************************************************/ + +//returns percentage reduction in size +static +float size_reduction(SddSize prev_size, SddSize cur_size) { + assert(cur_size <= prev_size); + if(prev_size==0) { + assert(cur_size==0); + return 0; + } + else return 100.0*(prev_size-cur_size)/prev_size; +} + +//returns new root +static +Vtree* sdd_vtree_minimize_limited_flag(Vtree* vtree, SddManager* manager, int limited) { + assert(manager->auto_vtree_search_on==0); + if(LEAF(vtree)) return vtree; + + manager->auto_vtree_search_on = 1; + + sdd_vtree_garbage_collect(vtree,manager); //local garbage collection + + //identify the subtree to minimize (heuristic) + Vtree* subtree = update_vtree_change(vtree,manager); //after gc + if(subtree==NULL) { + manager->auto_vtree_search_on = 0; + return vtree; + } + + Vtree** root_location = sdd_vtree_location(vtree,manager); //remember root location + SddSize init_size = sdd_vtree_live_size(subtree); + SddSize out_size = sdd_manager_live_size(manager)-init_size; + float threshold = manager->vtree_ops.convergence_threshold; + int iterations = 0; + float reduction; + + if(limited) { + start_search_limits(manager); + sdd_manager_init_vtree_size_limit(subtree,manager); //baseline for size limits (subtree better than vtree) + } + + assert(verify_X_constrained(manager->vtree)); + + do { //pass + SddSize prev_size = sdd_vtree_live_size(subtree); + subtree = local_search_pass(subtree,manager,limited); //possibly new root + SddSize cur_size = sdd_vtree_live_size(subtree); + reduction = size_reduction(prev_size,cur_size); + subtree = update_vtree_change_p(subtree,manager); //identify new subtree to minimize next + //update_vtree_change_p also updates parent's state + ++iterations; + } + while(!(limited && search_aborted(manager)) && subtree!=NULL && reduction>threshold); + + assert(verify_X_constrained(manager->vtree)); + + if(manager->auto_gc_and_search_on) { + SddSize final_size = sdd_manager_live_size(manager)-out_size; + float total_reduction = size_reduction(init_size,final_size); + manager->auto_search_iteration_count += iterations; + manager->auto_search_reduction_sum += total_reduction; + } + + manager->auto_vtree_search_on = 0; + if(limited) end_search_limits(manager); + assert(!FULL_DEBUG || verify_gc(*root_location,manager)); + return *root_location; //root may have changed due to rotations +} + +//unlimited +Vtree* sdd_vtree_minimize(Vtree* vtree, SddManager* manager) { + return sdd_vtree_minimize_limited_flag(vtree,manager,0); +} + +//limited +Vtree* sdd_vtree_minimize_limited(Vtree* vtree, SddManager* manager) { + return sdd_vtree_minimize_limited_flag(vtree,manager,1); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtree_search/state.c b/pysdd/lib/libsdd-2.0/src/vtree_search/state.c new file mode 100644 index 0000000..d768f49 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtree_search/state.c @@ -0,0 +1,127 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +/**************************************************************************************** + * The dynamic vtree search algorithms tries to be selective on which parts of a + * vtree it applies itself to. For this, it tries to identify parts of the vtree that + * were "unchanged" since the last search (iteration) and avoids these parts. + * + * The definition of "change" is heuristic and is based on three factors: the size of + * sdd nodes normalized for the vtree node, in addition to the identity of its left and + * right children. If none of these have changed since the last search iteration, the + * vtree node is considered unchanged. + * + * To implement this strategy, once must maintain a "state" with each vtree node to keep + * track of the previous size, left child, and right child. This is done using the + * VtreeSearchState structure in the code below. The field vtree->fold is set to 1 iff + * all the nodes in vtree are unchanged. + * + * Note: a leaf vtree node is considered unchanged iff its variable is being used + * by some decomposition sdd node. + * + * The search algorithm implements two strategies based on the notion of change: + * + * 1. The algorithm will apply itself to the smallest subtree with a changed vtree node + * 2. The algorithm will treat a subtree as a (virtual) leaf when all nodes in the + * vtree are unchanged + * + * The state of a vtree node is updated after each pass of the search algorithm. + ****************************************************************************************/ + +/**************************************************************************************** + * if there was no change in a vtree, treat it as a leaf node as far as search is + * concerned (i.e., do not try to change the vtree structure) + ****************************************************************************************/ + +int is_virtual_leaf_vtree(Vtree* vtree) { + VtreeSearchState* state = vtree->search_state; + return LEAF(vtree) || state->fold; +} + +/**************************************************************************************** + * returns the largest subtree which involved a change + * + * as a side effect: + * --updates the "fold" field for the search state + * --updates the "state" of each vtree node (size, parent, left child, right child) + * + ****************************************************************************************/ + +//vtree with no local changes will become a virtual leaf only when all its variables are used + +Vtree* update_vtree_change(Vtree* vtree, SddManager* manager) { + VtreeSearchState* state = vtree->search_state; + + if(LEAF(vtree)) { + state->fold = sdd_manager_is_var_used(vtree->var,manager); + //when fold==1, does not matter what is returned (vtree or NULL) + //when fold==0: + // --passing NULL: a vtree with no local changes will be pruned only if it has at most one fold=0 var + // --passing vtree: a vtree with no local changes will be pruned only if it has no fold=0 vars + // (i.e., when all its variables are used) + return vtree; + } + + //internal vtree node + Vtree* left = sdd_vtree_left(vtree); + Vtree* right = sdd_vtree_right(vtree); + + Vtree* pruned_left = update_vtree_change(left,manager); + Vtree* pruned_right = update_vtree_change(right,manager); + + VtreeSearchState* left_state = left->search_state; + VtreeSearchState* right_state = right->search_state; + + SddSize cur_size = sdd_vtree_live_size_at(vtree); + SddSize cur_count = sdd_vtree_live_count_at(vtree); + + int unchanged = (cur_size==state->previous_size) && + (cur_count==state->previous_count) && + (left==state->previous_left) && + (right==state->previous_right); + + if(!unchanged) { + //state has changed, save new one + state->previous_size = cur_size; + state->previous_count = cur_count; + state->previous_left = left; + state->previous_right = right; + } + + state->fold = (unchanged && left_state->fold && right_state->fold); + + //--folded vtree: NULL (prune it) + //--unfolded vtree: + // --local change: vtree (don't prune it) + // --no local change: + // --folded left, unfolded right: pruned_right (can be NULL) + // --unfolded left, folded right: pruned_left (can be NULL) + // --unfolded left, unfolded right: vtree + // --folded left, folded right: (impossible case) + // + if(state->fold) return NULL; + else if(!unchanged) return vtree; + else if(left_state->fold && !right_state->fold) return pruned_right; + else if(!left_state->fold && right_state->fold) return pruned_left; + else return vtree; +} + +//same as above function, except that it also updates the state of vtree's parent +Vtree* update_vtree_change_p(Vtree* vtree, SddManager* manager) { + Vtree* parent = sdd_vtree_parent(vtree); + if(parent!=NULL) { + VtreeSearchState* state = parent->search_state; + if(sdd_vtree_left(parent)==vtree) state->previous_left = vtree; + else state->previous_right = vtree; + } + return update_vtree_change(vtree,manager); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/compare.c b/pysdd/lib/libsdd-2.0/src/vtrees/compare.c new file mode 100644 index 0000000..0101201 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/compare.c @@ -0,0 +1,138 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +//local declarations +char cmp_vtrees(Vtree** lca, Vtree* vtree1, Vtree* vtree2); + +/**************************************************************************************** + * basic tests + ****************************************************************************************/ + +#if defined(WIN32) || defined(_WIN32) +#else +//tests whether vtree1 is a subtree of vtree2 +inline +int sdd_vtree_is_sub(const Vtree* vtree1, const Vtree* vtree2) { + return vtree1->position >= vtree2->first->position && vtree1->position <= vtree2->last->position; +} +#endif + +/**************************************************************************************** + * lca is the lowest common ancestor + ****************************************************************************************/ + +//returns the lca of vtree1 and vtree2 assuming both are sub_vtrees of root +Vtree* sdd_vtree_lca(Vtree* vtree1, Vtree* vtree2, Vtree* root) { + + //optimization + if(vtree1==vtree2) return vtree1; + else if(vtree1->parent==vtree2->parent) return vtree1->parent; + + SddLiteral p1 = vtree1->position; + SddLiteral p2 = vtree2->position; + + while(1) { + SddLiteral p = root->position; + if (p1 < p && p2 < p) root = root->left; + else if (p1 > p && p2 > p) root = root->right; + else return root; + } + +} + +//returns the least common ancestor of a set of variables in a vtree +//count is the number of variables +//variables: an array of size count containing the variables +Vtree* sdd_manager_lca_of_literals(int count, SddLiteral* literals, SddManager* manager) { + assert(count>0); + Vtree* root = manager->vtree; + Vtree* vtree = sdd_manager_vtree_of_var(labs(literals[0]),manager); + for(int i=1; i=2); + + Vtree* root = manager->vtree; + Vtree* l_lca = NULL; + Vtree* r_lca = NULL; + + for(SddElement* e=elements; eprime->vtree; + Vtree* s_vtree = e->sub->vtree; + assert(p_vtree); //prime cannot be trivial + + l_lca = (l_lca? sdd_vtree_lca(p_vtree,l_lca,root): p_vtree); + + if(s_vtree && r_lca) r_lca = sdd_vtree_lca(s_vtree,r_lca,root); + else if(s_vtree) r_lca = s_vtree; + } + + assert(l_lca && r_lca); + assert(l_lca->position < r_lca->position); + + Vtree* lca; + char c = cmp_vtrees(&lca,l_lca,r_lca); + + assert(c=='i'); + assert(lca); + + c ='i'; //to suppress compiler warning + return lca; +} + + +/**************************************************************************************** + * compare vtrees (assumes vtree1 <= vtree2 in the in-order) + * + * returns one of + * 'e': vtree1 and vtree2 are equal + * 'l': vtree1 is a sub_vtree of vtree2 (and, hence, of vtree2->left) + * 'r': vtree2 is a sub_vtree of vtree1 (and, hence, of vtree1->right) + * 'i': vtrees are incomparable + * + * sets vtree to lowest common ancestor of vtree1 and vtree2 + ****************************************************************************************/ + +//assumes vtree1 <= vtree2 in the in-order +char cmp_vtrees(Vtree** lca, Vtree* vtree1, Vtree* vtree2) { + assert(vtree1->position <= vtree2->position); + + if(vtree1==vtree2) { //equal vtrees + *lca = vtree1; + return 'e'; + } + else if(vtree1->position >= vtree2->first->position) { //vtree1 is a sub_vtree of vtree2->left + *lca = vtree2; + return 'l'; + } + else if(vtree2->position <= vtree1->last->position) { //vtree2 is a sub_vtree of vtree1->right + *lca = vtree1; + return 'r'; + } + else { //neither vtree is a sub_vtree of the other + *lca = vtree1->parent; + while(vtree2->position > (*lca)->last->position) { + //vtree2 not subtree of (*lca)->right + *lca = (*lca)->parent; + } + return 'i'; + } + +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/edit.c b/pysdd/lib/libsdd-2.0/src/vtrees/edit.c new file mode 100644 index 0000000..e83ff0d --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/edit.c @@ -0,0 +1,199 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//vtrees/vtree.c +Vtree* new_leaf_vtree(SddLiteral var); +Vtree* new_internal_vtree(Vtree* left_child, Vtree* right_child); +void set_vtree_properties(Vtree* vtree); + +/**************************************************************************************** + * utilities + ****************************************************************************************/ + +//returns +//'R' if vtree is root of manager vtree +//'l' if vtree is a left child of its parent +//'r' if vtree is a right child of its parent +static inline +char vtree_loc(Vtree* vtree) { + Vtree* parent = vtree->parent; + if(parent==NULL) return 'R'; //vtree is root + else if(parent->left==vtree) return 'l'; //vtree is left child + else return 'r'; //vtree is right child +} + +//location=='R' if old_vtree is root of manager vtree +//location=='l' if old_vtree is a left child of its parent +//location=='r' if old_vtree is a right child of its parent +//puts new_vtree in the location of old_vtree +static inline +void replace_vtree_with(Vtree* old_vtree, Vtree* new_vtree, char location, SddManager* manager) { + if(location=='R') { + new_vtree->parent = NULL; + manager->vtree = new_vtree; + } + else { + Vtree* parent = old_vtree->parent; + assert(parent!=NULL); + + //replace + if(location=='l') parent->left = new_vtree; + else parent->right = new_vtree; + old_vtree->parent = NULL; + new_vtree->parent = parent; + } +} + +//if on_left='l', make l_sibling a left child of parent, and r_sibling a right child +//if on_left='r', make r_sibling a left child of parent, and l_sibling a right child +static inline +void connect_children(Vtree* parent, Vtree* l_sibling, Vtree* r_sibling, char on_left) { + + //update parents + l_sibling->parent = parent; + r_sibling->parent = parent; + + //update children + if(on_left=='l') { + parent->left = l_sibling; + parent->right = r_sibling; + } + else { //on_left = 'r' + parent->left = r_sibling; + parent->right = l_sibling; + } +} + +/**************************************************************************************** + * add variable to vtree of manager + ****************************************************************************************/ + +//adds a new leaf vtree node corresponding to var +//if location = 'l', added leaf vtree node is to the left of sibling +//if location = 'r', added leaf vtree node is to the right of sibling +//returns added leaf node + +//assumes manager has at least one variable +Vtree* add_var_to_vtree(SddLiteral var, char location, Vtree* sibling, SddManager* manager) { + + assert(manager->var_count>0); + assert(location=='l' || location=='r'); + + //save parent as it will be erased by new_internal_vtree + Vtree* parent = sibling->parent; //could be NULL + + //construct new leaf node + Vtree* leaf = new_leaf_vtree(var); + + //construct new internal node (parent of sibling and new leaf vtree node) + Vtree* internal; + if(location=='l') internal = new_internal_vtree(leaf,sibling); + else internal = new_internal_vtree(sibling,leaf); //location = 'r' + + //replace sibling with internal as a child of parent + internal->parent = parent; //could be NULL + if(parent!=NULL) { + if(parent->left==sibling) parent->left = internal; + else parent->right = internal; + } + else manager->vtree = internal; //vtree has only a single node before addition + + //update properties to reflect new inorder and var counts + //CAN BE done more efficiently + set_vtree_properties(manager->vtree); + + return leaf; +} + +/**************************************************************************************** + * remove variable from vtree of manager + ****************************************************************************************/ + +void remove_var_from_vtree(SddLiteral var, SddManager* manager) { + + assert(manager->var_count>1); + + Vtree* leaf = sdd_manager_vtree_of_var(var,manager); + Vtree* parent = leaf->parent; + assert(parent!=NULL); + Vtree* sibling = parent->left==leaf? parent->right: parent->left; + + char location = vtree_loc(parent); + //location=='R' if parent is root of manager vtree + //location=='l' if it is a left child + //location=='r' if it is a right child + + replace_vtree_with(parent,sibling,location,manager); + //sibling has now assumed the location of parent in the vtree + //parent (and leaf) are now outside the vtree + + free(leaf); + free(parent); + + //update properties to reflect new inorder and var counts + //CAN BE done more efficiently + set_vtree_properties(manager->vtree); +} + +/**************************************************************************************** + * move variable in vtree of manager + ****************************************************************************************/ + +//move the leaf vtree corresponding to var so it becomes a sibling of new_sibling +//if var_location = 'l', move leaf to the left of new_sibling +//if var_location = 'r', move leaf to the right of new_sibling + +void move_var_in_vtree(SddLiteral var, char var_location, Vtree* new_sibling, SddManager* manager) { + + CHECK_ERROR(manager->var_count<=1,ERR_MSG_TWO_VARS,"move_var_in_vtree"); + CHECK_ERROR(sdd_manager_is_var_used(var,manager),ERR_MSG_MOV_VAR,"move_var_in_vtree"); + + assert(LEAF(new_sibling)); + + Vtree* leaf = sdd_manager_vtree_of_var(var,manager); + if(leaf==new_sibling) return; //already in requested location + + Vtree* parent = leaf->parent; + assert(parent!=NULL); + assert(parent->node_count==0); + Vtree* old_sibling = parent->left==leaf? parent->right: parent->left; + + //remove parent (and leaf) from its current location and replace with old_sibling + + char old_location = vtree_loc(parent); + //location=='R' if parent is root of manager vtree + //location=='l' if it is a left child + //location=='r' if it is a right child + + replace_vtree_with(parent,old_sibling,old_location,manager); + //old_sibling has now assumed the location of parent in the vtree (no longer pointing to parent) + //parent (and leaf) are now outside the vtree + + //remove new_sibling from its current location and replace with parent + + char new_location = vtree_loc(new_sibling); + replace_vtree_with(new_sibling,parent,new_location,manager); + //parent has now assumed the location of new_sibling in the vtree + //new_sibling is now outside the vtree + + //connect parent to its children according to var_location + //this must be done after replacement above so new_sibling has its parent during replacement + connect_children(parent,leaf,new_sibling,var_location); + //leaf and new_sibling are now back into the vtree + + //update properties to reflect new inorder and var counts + //CAN BE done more efficiently + set_vtree_properties(manager->vtree); +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/io.c b/pysdd/lib/libsdd-2.0/src/vtrees/io.c new file mode 100644 index 0000000..0caac31 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/io.c @@ -0,0 +1,207 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//vtrees/vtree.c +Vtree* new_leaf_vtree(SddLiteral var); +Vtree* new_internal_vtree(Vtree* left_child, Vtree* right_child); + +//local declarations +static Vtree* parse_vtree_file(char* buffer); + +/**************************************************************************************** + * reading vtrees from .vtree file + ****************************************************************************************/ + +//Reads a vtree from a .vtree file +Vtree* sdd_vtree_read(const char* filename) { + char* buffer = read_file(filename); + char* filtered = filter_comments(buffer); + Vtree* vtree = parse_vtree_file(filtered); + free(buffer); + free(filtered); + return vtree; +} + +/**************************************************************************************** + * printing vtrees to .vtree file + * + * the ->position of a vnode is used as its id to ensure an inorder labeling + ****************************************************************************************/ + +//prints a vtree node +void print_vtree_node(FILE* file, const Vtree* vnode) { + if(LEAF(vnode)) { + fprintf(file,"L %"PRIsS" %"PRIlitS"",vnode->position,vnode->var); + } + else { // internal node + print_vtree_node(file,vnode->left); + print_vtree_node(file,vnode->right); + fprintf(file,"I %"PRIsS" %"PRIsS" %"PRIsS"",vnode->position,vnode->left->position,vnode->right->position); + } + fprintf(file,"\n"); +} + +void print_vtree_header(FILE* file) { + static const char* header = + "c ids of vtree nodes start at 0\n" + "c ids of variables start at 1\n" + "c vtree nodes appear bottom-up, children before parents\n" + "c\n" + "c file syntax:\n" + "c vtree number-of-nodes-in-vtree\n" + "c L id-of-leaf-vtree-node id-of-variable\n" + "c I id-of-internal-vtree-node id-of-left-child id-of-right-child\n" + "c\n"; + fprintf(file,"%s",header); +} + +//prints a vtree in .vtree file format +void print_vtree(FILE* file, const Vtree* vtree) { + SddLiteral count = 2*(vtree->var_count) -1; + print_vtree_header(file); + fprintf(file,"vtree %"PRIsS"\n",count); + print_vtree_node(file,vtree); +} + +//saves vtree to file +void sdd_vtree_save(const char* fname, Vtree* vtree) { + FILE *file = fopen(fname,"w"); + print_vtree(file,vtree); + fclose(file); +} + +/**************************************************************************************** + * printing vtrees to .dot file + * + * the ->position of a vnode is used as its id to ensure an inorder labeling + ****************************************************************************************/ +extern SddManager* man; + +void print_vtree_nodes_as_dot(FILE* file, const Vtree* vtree) { + SddLiteral position = vtree->position; + char* shape = "plaintext"; + + if(LEAF(vtree)) { + SddLiteral var = vtree->var; + char* var_string = literal_to_label(var); + fprintf(file,"\nn%"PRIsS" [label=\"%s\",fontname=\"Times-Italic\"," + "fontsize=14,shape=\"%s\",fixedsize=true,width=.25,height=.25" + "]; ",position,var_string,shape); + free(var_string); + } + else { + fprintf(file,"\nn%"PRIsS" [label=\"%"PRIsS"\",fontname=\"Times\"," + "shape=\"%s\",fontsize=12,fixedsize=true,width=.2,height=.18]; ", + position,position,shape); + print_vtree_nodes_as_dot(file,vtree->left); + print_vtree_nodes_as_dot(file,vtree->right); + } +} + +void print_vtree_edges_as_dot(FILE* file, const Vtree* vtree, const Vtree* parent) { + SddLiteral position = vtree->position; + if(LEAF(vtree)) { + if(parent != NULL) { + SddLiteral parent_position = vtree->parent->position; + fprintf(file,"\nn%"PRIsS"->n%"PRIsS" [headclip=true,arrowhead=none,headlabel=\"%"PRIsS"\"," + "labelfontname=\"Times\",labelfontsize=10];",parent_position,position,position); + } + } + else { //internal node + if(parent != NULL) { + SddLiteral parent_position = vtree->parent->position; + fprintf(file,"\nn%"PRIsS"->n%"PRIsS" [arrowhead=none];",parent_position,position); + } + print_vtree_edges_as_dot(file,vtree->left,vtree); + print_vtree_edges_as_dot(file,vtree->right,vtree); // right first? + } +} + +//prints a vtree in .dot file format +void print_vtree_as_dot(FILE* file, const Vtree* vtree) { + fprintf(file,"\ndigraph vtree {"); + fprintf(file,"\n\noverlap=false"); + fprintf(file,"\n"); + + print_vtree_nodes_as_dot(file,vtree); + //allows printing vtrees of internal nodes (i.e., subtrees -- hence, the NULL below) + print_vtree_edges_as_dot(file,vtree,NULL); + + fprintf(file,"\n\n"); + fprintf(file,"\n}"); +} + +void sdd_vtree_save_as_dot(const char* fname, Vtree* vtree) { + FILE *file = fopen(fname,"w"); + print_vtree_as_dot(file,vtree); + fclose(file); +} + + +/**************************************************************************************** + * Parses a vtree from a .vtree cstring, where comments have been filtered out + * + * Returns root of vtree + ***************************************************************************************/ + +//->position is just used for temporary indexing --- its final value is irrelevant +Vtree* parse_vtree_file(char* buffer) { + //1st token is "vtree" + header_strtok(buffer,"vtree"); + + //read vtree node count + SddLiteral node_count = int_strtok(); + + //create a map from positions to vtree nodes + Vtree** vtree_node_list; + CALLOC(vtree_node_list,Vtree*,node_count,"parse_vtree_file"); + + Vtree* vnode = NULL; //to avoid compiler warning + for(SddLiteral count=0; countposition = position; + + //index constructed vtree node by its position + vtree_node_list[position] = vnode; + } + + free(vtree_node_list); + // last node is root + return vnode; +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/maps.c b/pysdd/lib/libsdd-2.0/src/vtrees/maps.c new file mode 100644 index 0000000..037e2be --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/maps.c @@ -0,0 +1,42 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//vtree nodes use zero-based indexing +//vtree variables use one-based indexing + +/**************************************************************************************** + * computes a map from vnode positions in inorder to vnodes + ****************************************************************************************/ + +//helper function for id2vtree +static +Vtree** fill_vtree_array(Vtree* vtree, Vtree** array) { + if(LEAF(vtree)) { + *array=vtree; + return array; + } + else { + Vtree** root_loc = 1+fill_vtree_array(vtree->left,array); + *root_loc = vtree; + return fill_vtree_array(vtree->right,1+root_loc); + } +} + +//returns an array that maps ids to their vnodes: array[id]=vnode +Vtree** pos2vnode_map(Vtree* vtree) { + SddSize count = 2*(vtree->var_count)-1; + Vtree** array; + CALLOC(array,Vtree*,count,"pos2vnode_map"); + fill_vtree_array(vtree,array); + return array; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/moves.c b/pysdd/lib/libsdd-2.0/src/vtrees/moves.c new file mode 100644 index 0000000..141c8bb --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/moves.c @@ -0,0 +1,156 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//vtrees/vtree.c +void update_positions_after_swap(Vtree* vtree); + +/**************************************************************************************** + * left rotation of a vtree node + ****************************************************************************************/ + +//w = (a x=(b c)) ===left rotation===> x = (w=(a b) c) +//x is left rotatable iff it is not leaf and it is a right child of its parent w +int is_left_rotatable(Vtree* x) { + return INTERNAL(x) && x->parent && x->parent->right==x; +} + +//w = (a x=(b c)) ===left rotation===> x = (w=(a b) c) +//rotation preserves inorder: awbxc ==> awbxc +void rotate_vtree_left(Vtree* x, SddManager* manager) { + + assert(is_left_rotatable(x)); + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); + + Vtree* w = x->parent; + Vtree* b = x->left; + Vtree* p = w->parent; + + //adjust pointers + w->right = b; + w->parent = x; + b->parent = w; + x->left = w; + x->parent = p; + if(p!=NULL) { if(w==p->left) p->left = x; else p->right = x; } + + //positions invariant + + //update linked list (next and prev invariant) + x->first = w->first; + w->last = b->last; + + //update var_count + w->var_count = w->left->var_count + w->right->var_count; //first + x->var_count = x->left->var_count + x->right->var_count; //second + + //update manager root + if(manager->vtree==w) manager->vtree = x; + + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); +} + + +/**************************************************************************************** + * right rotation of a vtree node + ****************************************************************************************/ + +//x = (w=(a b) c) ===right rotation===> w = (a x=(b c)) +//x right rotatable iff it is not leaf and its left child (w) is not leaf +int is_right_rotatable(Vtree* x) { + return INTERNAL(x) && INTERNAL(x->left); +} + +//x = (w=(a b) c) ===right rotation===> w = (a x=(b c)) +//rotation preserves inorder: awbxc ==> awbxc +void rotate_vtree_right(Vtree* x, SddManager* manager) { + + assert(is_right_rotatable(x)); + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); + + Vtree* w = x->left; + Vtree* b = w->right; + Vtree* p = x->parent; + + //adjust pointers + w->right = x; + w->parent = p; + b->parent = x; + x->left = b; + x->parent = w; + if(p!=NULL) { if(x==p->left) p->left = w; else p->right = w; } + + //positions invariant + + //update linked list (next and prev invariant) + x->first = b->first; + w->last = x->last; + + //update var_count + x->var_count = x->left->var_count + x->right->var_count; //first + w->var_count = w->left->var_count + w->right->var_count; //second + + //update manager root + if(manager->vtree==x) manager->vtree = w; + + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); +} + +/**************************************************************************************** + * Swapping vtree children + ****************************************************************************************/ + +#define LP(V) V==V->parent->left? V->parent: NULL +#define RP(V) V==V->parent->right? V->parent: NULL + +//swap children of a vtree node +void swap_vtree_children(Vtree* v, SddManager* manager) { + + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); + + //save boundaries + Vtree* p_boundary = v->first->prev; + Vtree* n_boundary = v->last->next; + + //switch children + SWAP(Vtree*,v->left,v->right); + + //var counts are invariant + + //update linked list + Vtree* left = v->left; + Vtree* right = v->right; + //first/last: 2 links (first/last of left/right are invariants) + v->first = left->first; + v->last = right->last; + //node v: 4 links + v->next = right->first; + v->prev = left->last; + v->next->prev = v->prev->next = v; + //boundaries of vtree v: 4 links + v->first->prev = p_boundary; + v->last->next = n_boundary; + if(p_boundary) p_boundary->next = v->first; + if(n_boundary) n_boundary->prev = v->last; + //some ancestors of v + //we only need to update ancestors that are in the same direction as v's parent + Vtree* p = v->parent; + if(p && v==p->left) do p->first = p->left->first; while (p->parent && (p=LP(p))); + else if(p) do p->last = p->right->last; while (p->parent && (p=RP(p))); + + //update vtree positions + update_positions_after_swap(v); //must be done after updating linked list + + assert(!FULL_DEBUG || verify_vtree_properties(manager->vtree)); +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/static.c b/pysdd/lib/libsdd-2.0/src/vtrees/static.c new file mode 100644 index 0000000..c01d372 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/static.c @@ -0,0 +1,270 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. + ****************************************************************************************/ + +#include "sdd.h" + +//declarations + +//vtrees/vtree.c +Vtree* new_leaf_vtree(SddLiteral var); +Vtree* new_internal_vtree(Vtree* left_child, Vtree* right_child); +void set_vtree_properties(Vtree* vtree); + +/**************************************************************************************** + * copy a vtree + ****************************************************************************************/ + +Vtree* copy_vtree(Vtree* vtree) { + Vtree* copy; + + if(LEAF(vtree)) copy = new_leaf_vtree(vtree->var); + else { + Vtree* left = copy_vtree(vtree->left); + Vtree* right = copy_vtree(vtree->right); + copy = new_internal_vtree(left,right); + } + + copy->some_X_constrained_vars = vtree->some_X_constrained_vars; + + return copy; +} + +/**************************************************************************************** + * constructing right linear vtree using natural order + ****************************************************************************************/ + +//constructs a right linear vtree with variables first_var, first_var+1, ..., last_var +static +Vtree* new_right_linear_vtree_aux(SddLiteral first_var, SddLiteral last_var) { + Vtree* leaf_vtree = new_leaf_vtree(first_var); + if(first_var==last_var) return leaf_vtree; + else { + Vtree* right = new_right_linear_vtree_aux(first_var+1,last_var); + return new_internal_vtree(leaf_vtree,right); + } +} + +//construct a right linear vtree with variables 1, 2, ..., var_count +static +Vtree* new_right_linear_vtree(SddLiteral var_count) { + return new_right_linear_vtree_aux(1,var_count); +} + +/**************************************************************************************** + * constructing left linear vtree using natural order + ****************************************************************************************/ + +//constructs a left linear vtree with variables first_var, first_var+1, ..., last_var +static +Vtree* new_left_linear_vtree_aux(SddLiteral first_var, SddLiteral last_var) { + Vtree* leaf_vtree = new_leaf_vtree(last_var); + if(first_var==last_var) return leaf_vtree; + else { + Vtree* left = new_left_linear_vtree_aux(first_var,last_var-1); + return new_internal_vtree(left,leaf_vtree); + } +} + +//constructs a left linear vtree with variables 1, 2, ..., var_count +static +Vtree* new_left_linear_vtree(SddLiteral var_count) { + return new_left_linear_vtree_aux(1,var_count); +} + +/**************************************************************************************** +* constructing vertical vtree using natural order: alternates between left and right linear +****************************************************************************************/ + +//constructs a vertical vtree (alternates between right-linear and left-linear) with +//variables first_var, first_var+1, ..., last_var +static +Vtree* new_vertical_vtree_aux(SddLiteral first_var, SddLiteral last_var, int is_left) { + Vtree* leaf_vtree = new_leaf_vtree(is_left? last_var: first_var); + if(first_var==last_var) return leaf_vtree; + else if(is_left) { + Vtree* left = new_vertical_vtree_aux(first_var,last_var-1,0); + return new_internal_vtree(left,leaf_vtree); + } + else { + Vtree* right = new_vertical_vtree_aux(first_var+1,last_var,1); + return new_internal_vtree(leaf_vtree,right); + } +} + +//constructs a vertical vtree (alternates between right-linear and left-linear) with +//variables 1, 2, ..., var_count +static +Vtree* new_vertical_vtree(SddLiteral var_count) { + return new_vertical_vtree_aux(1,var_count,0); +} + +/**************************************************************************************** +* constructing balanced vtree +****************************************************************************************/ + +//constructs a balanced vtree with variables first_var, first_var+1, ..., last_var +static +Vtree* new_balanced_vtree_aux(SddLiteral first_var, SddLiteral last_var) { + if(first_var==last_var) return new_leaf_vtree(first_var); + else { + SddLiteral mid_var = first_var + ((last_var-first_var+1)/2) -1; //integer division + Vtree* left = new_balanced_vtree_aux(first_var,mid_var); + Vtree* right = new_balanced_vtree_aux(mid_var+1,last_var); + return new_internal_vtree(left,right); + } +} + +//constructs a balanced vtree with variables 1, 2, ..., var_count +static +Vtree* new_balanced_vtree(SddLiteral var_count) { + return new_balanced_vtree_aux(1,var_count); +} + +/**************************************************************************************** +* constructing random vtree +****************************************************************************************/ + +Vtree* new_random_vtree_aux(SddLiteral var_count, SddLiteral* labels, SddLiteral* unused_count) { + assert(var_count >= 1); + assert(var_count <= *unused_count); + if(var_count==1) { + SddLiteral uc = *unused_count; + SddLiteral i = rand()%uc; //random index + SddLiteral var = labels[i]; //random label to use next + labels[i] = labels[uc-1]; //last label in array overrides used label + --(*unused_count); //one more used label + return new_leaf_vtree(var); + } + else { + assert(var_count >= 2); + //int balance = 35+rand()%31; //balance in [35..65] + //SddLiteral lc = MAX(1,round(var_count*(balance/100.0))); //random balanced + //SddLiteral lc = var_count/2; //balanced + SddLiteral lc = 1+rand()%(var_count-1); //random + assert(lc>0); + assert(lcvar = var_order[vtree->var-1]; + else { + replace_var_order_of_vtree(var_order,vtree->left); + replace_var_order_of_vtree(var_order,vtree->right); + } +} + +//returns a vtree with var_count variables +//the vtree dissects the variable order var_order[0]...var_order[var_count-1] +Vtree* sdd_vtree_new_with_var_order(SddLiteral var_count, SddLiteral* var_order, const char* type) { + Vtree* vtree = sdd_vtree_new(var_count,type); + replace_var_order_of_vtree(var_order,vtree); + return vtree; +} + +/**************************************************************************************** + * constructing X-constrained vtrees of different types, using natural variable order + * + * an X-constrained vtree is one in which there is a right-most vtree node that + * contains all variables not in X. vtree search will maintain this property + ****************************************************************************************/ + +//returns an X-constrained vtree +//var_count : number of variables in vtree +//is_X_var : array of size 1+var_count +//is_X_var[var]: 1 if var belongs to X, 0 otherwise +//is_X_var[0] : not used +// +//the number of variables in X must be less than var_count (can be 0) +Vtree* sdd_vtree_new_X_constrained(SddLiteral var_count, SddLiteral* is_X_var, const char* type) { + + SddLiteral X_count = 0; //count of variables in X + for(SddLiteral var=1; var<=var_count; var++) X_count += is_X_var[var]; + + //X cannot contain all vtree variables + assert(X_count < var_count); + + if(X_count==0) return sdd_vtree_new(var_count,type); //no X variables + + SddLiteral XP_count = var_count - X_count; //count of variables in complement of X + + //create variable order for vtree, with X vars first, then 0 (dummy), then XP vars + //variables appear in natural order within each of X and XP + SddLiteral start_X = 0; + SddLiteral start_XP = 1+X_count; + SddLiteral* var_order = (SddLiteral*) malloc((1+var_count)*sizeof(SddLiteral)); + + var_order[X_count] = 0; //dummy var + for(SddLiteral var=1; var<=var_count; var++) { + if(is_X_var[var]) var_order[start_X++] = var; + else var_order[start_XP++] = var; + } + assert(start_X==X_count && start_XP==1+var_count); + + Vtree* up_vtree = sdd_vtree_new_with_var_order(1+X_count,var_order,type); + Vtree* down_vtree = sdd_vtree_new_with_var_order(XP_count,var_order+X_count+1,type); + + free(var_order); + + Vtree* hook = up_vtree; + while(INTERNAL(hook->right)) hook = hook->right; + //hook->right now pointing to dummy leaf + + sdd_vtree_free(hook->right); + hook->right = down_vtree; + down_vtree->parent = hook; + hook->var_count = hook->left->var_count + hook->right->var_count; + + set_vtree_properties(up_vtree); + + //mark all vtree nodes that contain some variables in X + FOR_each_vtree_node(v,up_vtree,v->some_X_constrained_vars = 1); + FOR_each_vtree_node(v,down_vtree,v->some_X_constrained_vars = 0); + //nodes that only contain variables in XP will be marked 0 + + return up_vtree; +} + + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/libsdd-2.0/src/vtrees/vtree.c b/pysdd/lib/libsdd-2.0/src/vtrees/vtree.c new file mode 100644 index 0000000..a1e74a9 --- /dev/null +++ b/pysdd/lib/libsdd-2.0/src/vtrees/vtree.c @@ -0,0 +1,197 @@ +/**************************************************************************************** + * The Sentential Decision Diagram Package + * sdd version 2.0, January 8, 2018 + * http://reasoning.cs.ucla.edu/sdd + ****************************************************************************************/ + +#include "sdd.h" + +//vtree nodes use zero-based indexing +//vtree variables use one-based indexing + +/**************************************************************************************** + * constructing vtree nodes + ****************************************************************************************/ + +#define INITIALIZE_VTREE_FIELDS(vtree,left,right) {\ + vtree->parent = NULL;\ + vtree->nodes = NULL;\ + /* count and size */\ + vtree->sdd_size = 0;\ + vtree->dead_sdd_size = 0;\ + vtree->node_count = 0;\ + vtree->dead_node_count = 0;\ + /* bits */\ + vtree->some_X_constrained_vars = 0;\ + vtree->all_vars_in_sdd = 0;\ + vtree->no_var_in_sdd = 0;\ + vtree->bit = 0;\ + /* user fields */\ + vtree->user_data = NULL;\ + vtree->user_search_state = NULL;\ + vtree->user_bit = 0;\ + /* auto minimize mode */\ + vtree->auto_last_search_live_size = 0;\ + /* vtree search state (library)*/\ + VtreeSearchState* state = (VtreeSearchState*)malloc(sizeof(VtreeSearchState));\ + state->previous_left = left;\ + state->previous_right = right;\ + state->previous_size = 0;\ + state->previous_count = 0;\ + state->fold = 0;\ + vtree->search_state = state;\ +} + +//create a new leaf vtree node corresponding to var +Vtree* new_leaf_vtree(SddLiteral var) { + + Vtree* leaf; + MALLOC(leaf,Vtree,"new_leaf_vtree"); + + //vars + leaf->var = var; + leaf->var_count = 1; + //children + leaf->left = NULL; + leaf->right = NULL; + //other fields + INITIALIZE_VTREE_FIELDS(leaf,NULL,NULL); + + return leaf; +} + +//create a new internal vtree node with given children +Vtree* new_internal_vtree(Vtree* left_child, Vtree* right_child) { + + Vtree* internal; + MALLOC(internal,Vtree,"new_internal_vtree"); + + //vars + internal->var_count = left_child->var_count+right_child->var_count; + //children and parents + internal->left = left_child; + internal->right = right_child; + left_child->parent = internal; + right_child->parent = internal; + //other fields + INITIALIZE_VTREE_FIELDS(internal,left_child,right_child); + + return internal; +} + +/**************************************************************************************** + * freeing vtrees + ****************************************************************************************/ + +//freeing vtree and associated structures +void sdd_vtree_free(Vtree* vtree) { + if(INTERNAL(vtree)) { + sdd_vtree_free(vtree->left); + sdd_vtree_free(vtree->right); + } + //free vnode + free(vtree->search_state); + free(vtree); +} + +/**************************************************************************************** + * basic tests and lookups + ****************************************************************************************/ + +int sdd_vtree_is_leaf(const Vtree* vtree) { + return LEAF(vtree); +} + +//returns the leaf vtree which hosts var +//var is an integer > 0 +Vtree* sdd_manager_vtree_of_var(const SddLiteral var, const SddManager* manager) { + return manager->leaf_vtrees[var]; +} + +Vtree* sibling(Vtree* vtree) { + assert(vtree->parent!=NULL); + if(vtree==vtree->parent->left) return vtree->parent->right; + else return vtree->parent->left; +} + +//left-most leaf vtree +Vtree* first_leaf_vtree(Vtree* vtree) { + while(INTERNAL(vtree)) vtree = vtree->left; + return vtree; +} + +//right-most leaf vtree +Vtree* last_leaf_vtree(Vtree* vtree) { + while(INTERNAL(vtree)) vtree = vtree->right; + return vtree; +} + +//the location of the pointer for vtree +Vtree** sdd_vtree_location(Vtree* vtree, SddManager* manager) { + Vtree* parent = vtree->parent; + if(parent!=NULL) return (vtree==parent->left? &(parent->left): &(parent->right)); + else return &(manager->vtree); +} + + +/**************************************************************************************** + * adding/removing/moving variables will change positions and variable counts + ****************************************************************************************/ + +//sets the positions and var count of each vtree node +void set_vtree_properties(Vtree* vtree) { + void set_sub_vtree_properties(Vtree* vtree, SddLiteral start_position); + set_sub_vtree_properties(vtree,0); + assert(!FULL_DEBUG || verify_vtree_properties(vtree)); +} + +void set_sub_vtree_properties(Vtree* vtree, SddLiteral start_position) { + if(LEAF(vtree)) { + vtree->var_count = 1; + vtree->next = vtree->prev = NULL; + vtree->first = vtree->last = vtree; + vtree->position = start_position; + } + else { + Vtree* left = vtree->left; + Vtree* right = vtree->right; + + set_sub_vtree_properties(left,start_position); + set_sub_vtree_properties(right,2+left->last->position); + + //linked list + vtree->next = right->first; + vtree->prev = left->last; + left->last->next = vtree; + right->first->prev = vtree; + vtree->first = left->first; + vtree->last = right->last; + + //var count and position + vtree->var_count = left->var_count+right->var_count; + vtree->position = 1+left->last->position; + } +} + +/**************************************************************************************** + * maintaining vtree positions after its children have been swapped + ****************************************************************************************/ + +void update_positions_after_swap(Vtree* vtree) { + assert(INTERNAL(vtree)); + Vtree* left = vtree->left; + Vtree* right = vtree->right; + SddLiteral start_position = right->first->position; //was left->first->position before swap + //positions + vtree->position = start_position+(2*left->var_count-1); + SddLiteral loffset = start_position-left->first->position; + SddLiteral roffset = 1+vtree->position-right->first->position; + //update + FOR_each_vtree_node(v,left,v->position += loffset); + FOR_each_vtree_node(v,right,v->position += roffset); + assert(!FULL_DEBUG || verify_vtree_properties(vtree)); +} + +/**************************************************************************************** + * end + ****************************************************************************************/ diff --git a/pysdd/lib/sdd-2.0/include/compiler.h b/pysdd/lib/sdd-2.0/include/compiler.h index 3c9e40c..642d06e 100644 --- a/pysdd/lib/sdd-2.0/include/compiler.h +++ b/pysdd/lib/sdd-2.0/include/compiler.h @@ -2,11 +2,14 @@ * The Sentential Decision Diagram Package * sdd version 2.0, January 8, 2018 * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. ****************************************************************************************/ #include #include +#include "sddapi.h" + /**************************************************************************************** * this file contains macros, definitions of structures, and forward references * used by the fnf-to-sdd compiler (auto and manual versions) @@ -81,9 +84,9 @@ typedef Fnf Dnf; * function declaration ****************************************************************************************/ -Cnf* read_cnf(const char* filename); -Dnf* read_dnf(const char* filename); -void free_fnf(Fnf* fnf); +/* Cnf* read_cnf(const char* filename); */ +/* Dnf* read_dnf(const char* filename); */ +/* void free_fnf(Fnf* fnf); */ SddNode* fnf_to_sdd(Fnf* fnf, SddManager* manager); diff --git a/pysdd/lib/sdd-2.0/include/io.h b/pysdd/lib/sdd-2.0/include/io.h index 7920c48..558d302 100644 --- a/pysdd/lib/sdd-2.0/include/io.h +++ b/pysdd/lib/sdd-2.0/include/io.h @@ -1,4 +1,5 @@ +#include "compiler.h" #ifndef IO_H_ #define IO_H_ diff --git a/pysdd/lib/sdd-2.0/include/sddapi.h b/pysdd/lib/sdd-2.0/include/sddapi.h index ee92c2e..c79afcb 100644 --- a/pysdd/lib/sdd-2.0/include/sddapi.h +++ b/pysdd/lib/sdd-2.0/include/sddapi.h @@ -27,7 +27,7 @@ typedef size_t SddNodeSize; //size of decomposition for sdd nodes, changed to si typedef size_t SddRefCount; //refcount, changed to size_t for cross-platform compatibility typedef unsigned long long SddModelCount; //model counts typedef double SddWmc; // weighted model count -typedef long SddLiteral; //literals of clauses +typedef long long SddLiteral; //literals of clauses typedef char SddNodeType; //holds one of two values defined next //control strings diff --git a/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.a b/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.a deleted file mode 100644 index ea4142e..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.a and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.dylib b/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.dylib deleted file mode 100755 index 601d5cf..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Darwin-arm/libsdd.dylib and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Darwin/libsdd.a b/pysdd/lib/sdd-2.0/lib/Darwin/libsdd.a deleted file mode 100644 index a822426..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Darwin/libsdd.a and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Linux/libsdd.a b/pysdd/lib/sdd-2.0/lib/Linux/libsdd.a deleted file mode 100644 index 56bb608..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Linux/libsdd.a and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Linux/libsdd.so b/pysdd/lib/sdd-2.0/lib/Linux/libsdd.so deleted file mode 100755 index 4438f39..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Linux/libsdd.so and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Windows/sdd.dll b/pysdd/lib/sdd-2.0/lib/Windows/sdd.dll deleted file mode 100644 index 8e08ced..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Windows/sdd.dll and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/lib/Windows/sdd.lib b/pysdd/lib/sdd-2.0/lib/Windows/sdd.lib deleted file mode 100644 index 13bc68c..0000000 Binary files a/pysdd/lib/sdd-2.0/lib/Windows/sdd.lib and /dev/null differ diff --git a/pysdd/lib/sdd-2.0/src/fnf/compiler.c b/pysdd/lib/sdd-2.0/src/fnf/compiler.c index d8b1d9c..926c059 100644 --- a/pysdd/lib/sdd-2.0/src/fnf/compiler.c +++ b/pysdd/lib/sdd-2.0/src/fnf/compiler.c @@ -2,6 +2,7 @@ * The Sentential Decision Diagram Package * sdd version 2.0, January 8, 2018 * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. ****************************************************************************************/ #include "sddapi.h" diff --git a/pysdd/lib/sdd-2.0/src/fnf/fnf.c b/pysdd/lib/sdd-2.0/src/fnf/fnf.c index 96863a5..e4b6c12 100644 --- a/pysdd/lib/sdd-2.0/src/fnf/fnf.c +++ b/pysdd/lib/sdd-2.0/src/fnf/fnf.c @@ -2,6 +2,7 @@ * The Sentential Decision Diagram Package * sdd version 2.0, January 8, 2018 * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. ****************************************************************************************/ #include "sddapi.h" diff --git a/pysdd/lib/sdd-2.0/src/fnf/io.c b/pysdd/lib/sdd-2.0/src/fnf/io.c index 5cbba30..8cb5288 100644 --- a/pysdd/lib/sdd-2.0/src/fnf/io.c +++ b/pysdd/lib/sdd-2.0/src/fnf/io.c @@ -2,6 +2,7 @@ * The Sentential Decision Diagram Package * sdd version 2.0, January 8, 2018 * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. ****************************************************************************************/ #include @@ -26,14 +27,14 @@ static char* read_file(const char* filename) { // lookup file size fseek(file,0,SEEK_END); - unsigned int file_size = ftell(file); + size_t file_size = ftell(file); rewind(file); // allocate memory char* buffer = (char*)calloc(file_size+1,sizeof(char)); // read the whole file - unsigned int result = fread(buffer,sizeof(char),file_size,file); + size_t result = fread(buffer,sizeof(char),file_size,file); if (result != file_size) { printf("Could not read the file %s\n",filename); exit(1); diff --git a/pysdd/lib/sdd-2.0/src/fnf/utils.c b/pysdd/lib/sdd-2.0/src/fnf/utils.c index 93d637e..e79b8f5 100644 --- a/pysdd/lib/sdd-2.0/src/fnf/utils.c +++ b/pysdd/lib/sdd-2.0/src/fnf/utils.c @@ -2,6 +2,7 @@ * The Sentential Decision Diagram Package * sdd version 2.0, January 8, 2018 * http://reasoning.cs.ucla.edu/sdd + * Modified by DTAI. ****************************************************************************************/ #include "sddapi.h" diff --git a/pysdd/lib/sdd-2.0/src/getopt.c b/pysdd/lib/sdd-2.0/src/getopt.c deleted file mode 100644 index 21b30dc..0000000 --- a/pysdd/lib/sdd-2.0/src/getopt.c +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************************** - * The Sentential Decision Diagram Package - * sdd version 2.0, January 8, 2018 - * http://reasoning.cs.ucla.edu/sdd - ****************************************************************************************/ - -#include -#include -#include -#include -#include "sddapi.h" -#include "compiler.h" - -void print_help(const char* PACKAGE, int exit_value); -const char* libsdd_version(); - -/**************************************************************************************** - * start - ****************************************************************************************/ - -SddCompilerOptions sdd_getopt(int argc, char **argv) { - char* PACKAGE = "sdd"; - - //default options - SddCompilerOptions options = - { - NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, //file names - 0, //flag - "balanced", //vtree type - -1, //vtree search mode - 0, //post-compilation search - 0 //verbose - }; - int option; - int input_count = 0; - - while ((option = getopt(argc,argv,"c:d:v:s:W:V:R:S:hmt:r:qp")) != -1) { - switch (option) { - //input - case 'c': - options.cnf_filename = optarg; - input_count++; - break; - case 'd': - options.dnf_filename = optarg; - input_count++; - break; - case 'v': - options.vtree_filename = optarg; - break; - case 's': - options.sdd_filename = optarg; - input_count++; - break; - //output - case 'W': - options.output_vtree_filename = optarg; - break; - case 'V': - options.output_vtree_dot_filename = optarg; - break; - case 'R': - options.output_sdd_filename = optarg; - break; - case 'S': - options.output_sdd_dot_filename = optarg; - break; - //flags - case 'h': //HELP - print_help(PACKAGE,0); - break; - case 'm': - options.minimize_cardinality = 1; - break; - //options with arguments - case 't': - options.initial_vtree_type = optarg; - break; - case 'r': - options.vtree_search_mode = strtol(optarg,NULL,10); - break; - case 'q': - options.post_search = 1; - break; - case 'p': - options.verbose = 1; - break; - default: - print_help(PACKAGE,1); - } - } - - //checking validity of options - if(input_count != 1) { - fprintf(stderr, "%s: must specify exactly one of a cnf, dnf or sdd file\n",PACKAGE); - print_help(PACKAGE,1); - } - if (options.sdd_filename!=NULL && options.vtree_filename==NULL) { - fprintf(stderr, "%s: sdd file must be specified with vtree file\n",PACKAGE); - print_help(PACKAGE,1); - } - if(strcmp(options.initial_vtree_type,"left") && - strcmp(options.initial_vtree_type,"right") && - strcmp(options.initial_vtree_type,"vertical") && - strcmp(options.initial_vtree_type,"balanced") && - strcmp(options.initial_vtree_type,"random")) { - fprintf(stderr, "%s: initial vtree type must be one of: left, right, vertical, balanced, or random\n",PACKAGE); - print_help(PACKAGE,1); - } - - return options; -} - - -void print_help(const char* PACKAGE, int exit_value) { - printf("%s: Sentential Decision Diagram, Compiler\n", PACKAGE); - printf("%s\n",libsdd_version()); - printf("%s (-c FILE | -d FILE | -s FILE) [-v FILE | -t TYPE] [-WVRS FILE] [-r MODE] [-mhqp]\n", PACKAGE); - - printf(" -c FILE set input CNF file\n"); - printf(" -d FILE set input DNF file\n"); - printf(" -v FILE set input VTREE file\n"); - printf(" -s FILE set input SDD file\n"); - - printf(" -W FILE set output VTREE file\n"); - printf(" -V FILE set output VTREE (dot) file\n"); - printf(" -R FILE set output SDD file\n"); - printf(" -S FILE set output SDD (dot) file\n"); - - printf(" -m minimize the cardinality of compiled sdd\n"); - - printf(" -t TYPE set initial vtree type (left/right/vertical/balanced/random)\n"); - - printf(" -r K if K>0: invoke vtree search every K clauses\n"); - printf(" if K=0: disable vtree search\n"); - printf(" by default (no -r option), dynamic vtree search is enabled\n"); - printf(" -q perform post-compilation vtree search\n"); - - printf(" -h print this help and exit\n"); - printf(" -p verbose output\n"); - exit(exit_value); -} - -/**************************************************************************************** - * end - ****************************************************************************************/ diff --git a/pysdd/lib/sdd-2.0/src/main.c b/pysdd/lib/sdd-2.0/src/main.c deleted file mode 100644 index a202635..0000000 --- a/pysdd/lib/sdd-2.0/src/main.c +++ /dev/null @@ -1,157 +0,0 @@ -/**************************************************************************************** - * The Sentential Decision Diagram Package - * sdd version 2.0, January 8, 2018 - * http://reasoning.cs.ucla.edu/sdd - ****************************************************************************************/ - -#include -#include "sddapi.h" -#include "compiler.h" - -// forward references -SddCompilerOptions sdd_getopt(int argc, char **argv); // in getopt.c -char* ppc(SddSize n); // pretty print (in libsdd: util.c) - -void print_node(SddNode* node, SddManager* manager) { - char* s = NULL; - printf( " sdd size : %s \n",s=ppc(sdd_size(node))); free(s); - printf( " sdd node count : %s \n",s=ppc(sdd_count(node))); free(s); - - clock_t c1, c2; - c1 = clock(); - SddModelCount mc = sdd_global_model_count(node,manager); - c2 = clock(); - printf( " sdd model count : %s %.3f sec\n",s=ppc(mc),(float)(c2-c1)/CLOCKS_PER_SEC); free(s); -} - -/**************************************************************************************** - * start - ****************************************************************************************/ - -int main(int argc, char** argv) { - - //get options from command line (and defaults) - SddCompilerOptions options = sdd_getopt(argc,argv); - - Fnf* fnf = NULL; - Vtree* vtree; - SddNode* node; - SddManager* manager; - clock_t c1, c2; - char* s; - - if(options.cnf_filename!=NULL) { - printf("\nreading cnf..."); - fnf = read_cnf(options.cnf_filename); - printf("vars=%"PRIlitS" clauses=%"PRIsS"",fnf->var_count,fnf->litset_count); - } - else if(options.dnf_filename!=NULL) { - printf("\nreading dnf..."); - fnf = read_dnf(options.dnf_filename); - printf("vars=%"PRIlitS" terms=%"PRIsS"",fnf->var_count,fnf->litset_count); - } - - if(options.vtree_filename!=NULL) { - printf("\nreading initial vtree..."); - vtree = sdd_vtree_read(options.vtree_filename); - } - else { - printf("\ncreating initial vtree (%s)...",options.initial_vtree_type); - vtree = sdd_vtree_new(fnf->var_count,options.initial_vtree_type); - } - - printf("\ncreating manager..."); - //create manager - manager = sdd_manager_new(vtree); - //no longer needed - sdd_vtree_free(vtree); - //passing compiler options to manager - sdd_manager_set_options(&options,manager); - - if(options.sdd_filename==NULL) { - printf("\ncompiling..."); fflush(stdout); - c1 = clock(); - node = fnf_to_sdd(fnf,manager); - c2 = clock(); - float secs = (float)(c2-c1)/CLOCKS_PER_SEC; - printf("\n\ncompilation time : %.3f sec\n",secs); - } else { - printf("\nreading sdd from file..."); fflush(stdout); - c1 = clock(); - node = sdd_read(options.sdd_filename,manager); - c2 = clock(); - float secs = (float)(c2-c1)/CLOCKS_PER_SEC; - printf("\n\nread time : %.3f sec\n",secs); - } - - print_node(node,manager); - if(options.verbose) - sdd_manager_print(manager); - - if(options.minimize_cardinality) { - printf("\nminimizing cardinality..."); fflush(stdout); - c1 = clock(); - node = sdd_global_minimize_cardinality(node,manager); - c2 = clock(); - SddLiteral min_card = sdd_minimum_cardinality(node); - printf("\n"); - print_node(node,manager); - printf(" min cardinality : %ld %.3f sec\n",min_card,(float)(c2-c1)/CLOCKS_PER_SEC); - } - - Vtree* manager_vtree = sdd_manager_vtree(manager); - - if(options.post_search==1) { - sdd_ref(node,manager); - printf("\ndynamic vtree (post compilation)\n"); - printf( " sdd initial size : %s\n",s=ppc(sdd_size(node))); free(s); - fflush(stdout); - c1 = clock(); - sdd_manager_minimize_limited(manager); - c2 = clock(); - printf("\n"); - printf( " dynamic vtree time : %.3f sec\n",(float)(c2-c1)/CLOCKS_PER_SEC); - print_node(node,manager); - sdd_deref(node,manager); - if(options.verbose) - sdd_manager_print(manager); - } - - if(options.output_sdd_filename != NULL) { - printf("saving compiled sdd ..."); - sdd_save(options.output_sdd_filename,node); - printf("done\n"); - } - - if(options.output_sdd_dot_filename != NULL) { - printf("saving compiled sdd (dot)..."); - sdd_save_as_dot(options.output_sdd_dot_filename,node); - printf("done\n"); - } - - if(options.output_vtree_filename != NULL) { - printf("saving vtree..."); - sdd_vtree_save(options.output_vtree_filename,manager_vtree); - printf("done\n"); - } - - if(options.output_vtree_dot_filename != NULL) { - printf("saving vtree (dot)..."); - sdd_vtree_save_as_dot(options.output_vtree_dot_filename,manager_vtree); - printf("done\n"); - } - - printf("freeing..."); - fflush(stdout); - if(options.cnf_filename!=NULL || options.dnf_filename!=NULL) { - free_fnf(fnf); - } - sdd_manager_free(manager); - printf("done\n"); - - return 0; -} - -/**************************************************************************************** - * end - ****************************************************************************************/ diff --git a/requirements.txt b/requirements.txt index ecce27b..7933e57 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ cython numpy -cysignals \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 657da44..0000000 --- a/setup.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: UTF-8 -*- -""" -setup.py -~~~~~~~~ - -Usage: python3 setup.py build_ext --inplace - -:author: Wannes Meert -:copyright: Copyright 2017-2023 KU Leuven and Regents of the University of California. -:license: Apache License, Version 2.0, see LICENSE for details. -""" -from setuptools import setup -from setuptools.extension import Extension -from setuptools.command.build_ext import build_ext as BuildExtCommand -from setuptools import Distribution -from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError -import platform -import os -import re -from pathlib import Path - - -try: - from Cython.Build import cythonize -except ImportError: - cythonize = None - -try: - import cysignals -except ImportError as exc: - print(f"cysignals not found\n{exc}") - cysignals = None - -print("Python version = " + str(platform.python_version())) - - -class MyDistribution(Distribution): - global_options = Distribution.global_options + [ - ('debug', None, 'Compile with debug options on (PySDD option)'), - ('usecysignals', None, 'Compile with CySignals (PySDD option)') - ] - - def __init__(self, attrs=None): - self.debug = 0 - self.usecysignals = 0 - super().__init__(attrs) - - -# build_type = "debug" -build_type = "optimized" - -here = Path(".") # setup script requires relative paths - -with (here / "pysdd" / "__init__.py").open('r') as fd: - wrapper_version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) -if not wrapper_version: - raise RuntimeError('Cannot find version information') - -sdd_version = "2.0" - -libwrapper_path = here / "pysdd" / "lib" -sdd_path = libwrapper_path / f"sdd-{sdd_version}" -lib_path = sdd_path / "lib" -inc_path = sdd_path / "include" -src_path = sdd_path / "src" -csrc_path = here / "pysdd" / "src" -# c_files_paths = src_path.glob("**/*.c") -c_files_paths = (src_path / "fnf").glob("*.c") - -sdd_extra_inc_path = libwrapper_path / "sdd_extra" / "include" - -# weight optimization wrapper -wo_path = libwrapper_path / "weight_optimization" -wo_inc_path = wo_path / "include" -wo_src_path = wo_path / "src" -wo_c_files_paths = wo_src_path.glob("*.c") - -c_dirs_paths = set(p.parent for p in src_path.glob("**/*.c")) | {wo_src_path} -all_c_file_paths = [str(p) for p in c_files_paths] + [str(p) for p in wo_c_files_paths] -# print("Found c files: ", ", ".join([str(p) for p in all_c_file_paths])) - -os.environ["LDFLAGS"] = f"-L{lib_path}" -os.environ["CPPFLAGS"] = f"-I{inc_path} " + f"-I{wo_inc_path} " + f"-I{sdd_extra_inc_path} " + f"-I{csrc_path} " + \ - " ".join(f"-I{p}" for p in c_dirs_paths) -library_dirs = [str(lib_path)] -include_dirs = [str(inc_path), str(wo_inc_path), str(sdd_extra_inc_path), str(csrc_path)] + \ - [str(p) for p in c_dirs_paths] - -compile_time_env = {'HAVE_CYSIGNALS': False} -# if cysignals is not None: -# compile_time_env['HAVE_CYSIGNALS'] = True - -c_args = { - 'unix': ['-O3', '-Wall'], - 'msvc': ['/Ox', '/fp:fast', '/favor:INTEL64', '/Og'], - 'mingw32': ['-O3', '-march=native'] -} -c_args_debug = { - 'unix': ["-O0", '-g'], - 'msvc': [["-Zi", "/Od"]], - 'mingw32': ["-march=native", "-O0", '-g'] -} -l_args = { - 'unix': [], - 'msvc': [], - 'mingw32': ['-static-libgcc', '-static-libstdc++', '-Wl,-Bstatic,--whole-archive', - '-lwinpthread', '-Wl,--no-whole-archive'] -} -l_args_debug = { - 'unix': ['-g'], - 'msvc': ["-debug"], - 'mingw32': ['-g'] -} - - -class MyBuildExtCommand(BuildExtCommand): - - def build_extensions(self): - global lib_path - try: - c = self.compiler.compiler_type - print("Compiler type: {}".format(c)) - compiler_name = self.compiler.compiler[0] - print("Compiler name: {}".format(compiler_name)) - except AttributeError as exc: - output = str(exc) - if 'MSVCCompiler' in output: - c = "msvc" - print("Compiler type: {}".format(c)) - compiler_name = "MSVCCompiler" - print("Compiler name: {}".format(compiler_name)) - else: - print("Could not derive compiler type") - c = "Unknown" - compiler_name = "Unknown" - print("--debug: {}".format(self.distribution.debug)) - print("--usecysignals: {}".format(self.distribution.usecysignals)) - # Compiler and linker options - if self.distribution.debug: - self.force = True # force full rebuild in debugging mode - cur_c_args = c_args_debug - cur_l_args = l_args_debug - else: - cur_c_args = c_args - cur_l_args = l_args - if "gcc" in compiler_name: - cur_c_args["unix"].append("-std=c99") - if c in cur_c_args: - args = cur_c_args[c] - for e in self.extensions: # type: Extension - e.extra_compile_args = args - else: - print("Unknown compiler type: {}".format(c)) - if c in cur_l_args: - args = cur_l_args[c] - for e in self.extensions: # type: Extension - e.extra_link_args = args - if self.distribution.usecysignals: - if cysignals is not None: - if self.cython_compile_time_env is None: - self.cython_compile_time_env = {'HAVE_CYSIGNALS': True} - else: - self.cython_compile_time_env['HAVE_CYSIGNALS'] = True - else: - print("Warning: import cysignals failed") - # Extra objects - if "Darwin" in platform.system(): - if "arm" in platform.platform(): - cur_lib_path = lib_path / "Darwin-arm" - else: - cur_lib_path = lib_path / "Darwin" - if build_type == "debug": - cur_lib_path = cur_lib_path / "debug" - libsdd_path = cur_lib_path / "libsdd.a" - elif "Linux" in platform.system(): - cur_lib_path = lib_path / "Linux" - libsdd_path = cur_lib_path / "libsdd.a" - elif "Windows" in platform.system(): - cur_lib_path = lib_path / "Windows" - libsdd_path = cur_lib_path / "sdd.lib" - else: - libsdd_path = lib_path / "libsdd.a" - for e in self.extensions: # type: Extension - e.extra_objects = [str(libsdd_path)] - BuildExtCommand.build_extensions(self) - - -if cythonize is not None: - ext_modules = cythonize([ - Extension( - "pysdd.sdd", [str(here / "pysdd" / "sdd.pyx")] + all_c_file_paths, - include_dirs=include_dirs, - library_dirs=library_dirs - # extra_objects=[str(libsdd_path)], - # extra_compile_args=extra_compile_args, - # extra_link_args=extra_link_args - # include_dirs=[numpy.get_include()] - )], - compiler_directives={'embedsignature': True}, - # gdb_debug=gdb_debug, - compile_time_env=compile_time_env) -else: - ext_modules = [] - print('****************************************') - print('Cython not yet available, cannot compile') - print('****************************************') - raise ImportError('Cython not available') - -# install_requires = ['numpy', 'cython'] -install_requires = ['cython>=3.0.0'] -setup_requires = ['setuptools>=18.0', 'cython>=3.0.0'] -tests_require = ['pytest'] -dev_require = tests_require + ['cython>=3.0.0'] - -with (here / 'README.rst').open('r', encoding='utf-8') as f: - long_description = f.read() - -setup_kwargs = {} - - -def set_setup_kwargs(**kwargs): - global setup_kwargs - setup_kwargs = kwargs - - -set_setup_kwargs( - name='PySDD', - version=wrapper_version, - description='Sentential Decision Diagrams', - long_description=long_description, - author='Wannes Meert, Arthur Choi', - author_email='wannes.meert@cs.kuleuven.be', - url='https://github.com/wannesm/PySDD', - project_urls={ - 'PySDD documentation': 'https://pysdd.readthedocs.io/en/latest/', - 'PySDD source': 'https://github.com/wannesm/PySDD' - }, - packages=["pysdd"], - install_requires=install_requires, - setup_requires=setup_requires, - tests_require=tests_require, - extras_require={ - 'all': ['cysignals', 'numpy'], - 'dev': dev_require - }, - include_package_data=True, - package_data={ - '': ['*.pyx', '*.pxd', '*.h', '*.c', '*.so', '*.a', '*.dll', '*.dylib', '*.lib'], - }, - distclass=MyDistribution, - cmdclass={ - 'build_ext': MyBuildExtCommand - }, - entry_points={ - 'console_scripts': [ - 'pysdd = pysdd.cli:main' - ]}, - python_requires='>=3.6', - license='Apache 2.0', - classifiers=[ - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 3', - 'Topic :: Scientific/Engineering :: Artificial Intelligence' - ], - keywords='sdd, knowledge compilation', - ext_modules=ext_modules, - zip_safe=False -) - -try: - setup(**setup_kwargs) -except (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError, SystemExit) as exc: - print("********************************************") - print("ERROR: The C extension could not be compiled") - print("********************************************") - print(exc) - raise exc diff --git a/tests/__pycache__/.gitignore b/tests/__pycache__/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/tests/__pycache__/.gitignore @@ -0,0 +1 @@ +*.pyc