Build formula

Example 1: Build simple formula

#!/usr/bin/env python3
from pysdd.sdd import SddManager, Vtree


def main():
    # set up vtree and manager
    var_count = 4
    var_order = [2,1,4,3]
    vtree_type = "balanced"

    vtree = Vtree(var_count, var_order, vtree_type)
    manager = SddManager.from_vtree(vtree)

    # construct a formula (A^B)v(B^C)v(C^D)
    print("constructing SDD ... ")
    a, b, c, d = [manager.literal(i) for i in range(1, 5)]
    alpha = (a & b) | (b & c) | (c & d)
    print("done")

    print("saving sdd and vtree ... ")
    with open("output/sdd.dot", "w") as out:
        print(alpha.dot(), file=out)
    with open("output/vtree.dot", "w") as out:
        print(vtree.dot(), file=out)
    print("done")


if __name__ == "__main__":
    main()

Example 2: Garbage collection

#!/usr/bin/env python3
from pathlib import Path
from pysdd.sdd import SddManager, Vtree


def main():

  # set up vtree and manager
  var_count = 4
  vtree_type = "right".encode()
  vtree = Vtree(var_count=var_count, vtree_type=vtree_type)
  manager = SddManager(vtree=vtree)

  x = [None] + [manager.literal(i) for i in range(1,5)]

  # construct the term X_1 ^ X_2 ^ X_3 ^ X_4
  alpha =  x[1] &  x[2] & x[3] & x[4]

  # construct the term ~X_1 ^ X_2 ^ X_3 ^ X_4
  beta  = ~x[1] &  x[2] & x[3] & x[4]

  # construct the term ~X_1 ^ ~X_2 ^ X_3 ^ X_4
  gamma = ~x[1] & ~x[2] & x[3] & x[4]

  print("== before referencing:")
  print(f"  live sdd size = {manager.live_size()}")
  print(f"  dead sdd size = {manager.dead_size()}")

  # ref SDDs so that they are not garbage collected
  alpha.ref()
  beta.ref()
  gamma.ref()
  print("== after referencing:")
  print(f"  live sdd size = {manager.live_size()}")
  print(f"  dead sdd size = {manager.dead_size()}")

  # garbage collect
  manager.garbage_collect()
  print("== after garbage collection:");
  print(f"  live sdd size = {manager.live_size()}")
  print(f"  dead sdd size = {manager.dead_size()}")

  alpha.deref()
  beta.deref()
  gamma.deref()

  print("saving vtree & shared sdd ...")
  if not Path("output").is_dir():
      raise Exception(f"Directory 'output' does not exist")
  vtree.save_as_dot("output/shared-vtree.dot".encode())
  manager.shared_save_as_dot("output/shared.dot".encode())


if __name__ == "__main__":
    main()

Example 3: Minimization

#!/usr/bin/env python3
from pathlib import Path
from pysdd.sdd import SddManager, Vtree


def main():

  # set up vtree and manager
  vtree = Vtree.from_file("input/opt-swap.vtree".encode())
  manager = SddManager.from_vtree(vtree)

  print("reading sdd from file ...")
  alpha = manager.read_sdd_file("input/opt-swap.sdd".encode())
  print(f"  sdd size = {alpha.size()}")

  # ref, perform the minimization, and then de-ref
  alpha.ref()
  print("minimizing sdd size ... ", end="")
  manager.minimize()  # see also manager.minimize_limited()
  print("done!")
  print(f"  sdd size = {alpha.size()}")
  alpha.deref()

  # augment the SDD
  print("augmenting sdd ...")
  beta = alpha * (manager.l(4) + manager.l(5))
  print(f"  sdd size = {beta.size()}")

  # ref, perform the minimization again on new SDD, and then de-ref
  beta.ref()
  print("minimizing sdd ... ", end="")
  manager.minimize()
  print("done!")
  print(f"  sdd size = {beta.size()}")
  beta.deref()


if __name__ == "__main__":
    main()


Example 4: Vtree rotations

#!/usr/bin/env python3
from pathlib import Path
from pysdd.sdd import SddManager, Vtree


here = Path(__file__).parent


def main():

  # set up vtree and manager
  vtree = Vtree.from_file(bytes(here / "input" / "rotate-left.vtree"))
  manager = SddManager.from_vtree(vtree)

  # construct the term X_1 ^ X_2 ^ X_3 ^ X_4
  x = [None] + [manager.literal(i) for i in range(1, 5)]
  alpha = x[1]*x[2]*x[3]*x[4]

  # to perform a rotate, we need the manager's vtree
  manager_vtree = manager.vtree()
  manager_vtree_right = manager_vtree.right()

  print("saving vtree & sdd ...")
  if not Path("output").is_dir():
      raise Exception(f"Directory 'output' does not exist")
  manager_vtree.save_as_dot("output/before-rotate-vtree.dot".encode())
  alpha.save_as_dot("output/before-rotate-sdd.dot".encode())

  # ref alpha (so it is not gc'd)
  alpha.ref()

  # garbage collect (no dead nodes when performing vtree operations)
  print(f"dead sdd nodes = {manager.dead_count()}")
  print("garbage collection ...")
  manager.garbage_collect()
  print(f"dead sdd nodes = {manager.dead_count()}")

  print("left rotating ... ", end="")
  succeeded = manager_vtree_right.rotate_left(manager, 0)
  print("succeeded!" if succeeded == 1 else "did not succeed!")

  # deref alpha, since ref's are no longer needed
  alpha.deref()

  # the root changed after rotation, so get the manager's vtree again
  # this time using root_location
  manager_vtree = manager.vtree()

  print("saving vtree & sdd ...")
  if not Path("output").is_dir():
      raise Exception(f"Directory 'output' does not exist")
  manager_vtree.save_as_dot("output/after-rotate-vtree.dot".encode())
  alpha.save_as_dot("output/after-rotate-sdd.dot".encode())


if __name__ == "__main__":
    main()



Example 5: Modifying vtree

#!/usr/bin/env python3
from pathlib import Path
from pysdd.sdd import SddManager, Vtree


here = Path(__file__).parent


def main():

  # set up vtree and manager
  vtree = Vtree.from_file(bytes(here / "input" / "big-swap.vtree"))
  manager = SddManager.from_vtree(vtree)

  print("reading sdd from file ...")
  alpha = manager.read_sdd_file("input/big-swap.sdd".encode())
  print(f"  sdd size = {alpha.size()}")

  # to perform a swap, we need the manager's vtree
  manager_vtree = manager.vtree()

  # ref alpha (no dead nodes when swapping)
  alpha.ref()

  # using size of sdd normalized for manager_vtree as baseline for limit
  manager.init_vtree_size_limit(manager_vtree)

  limit = 2.0
  manager.set_vtree_operation_size_limit(limit)

  print(f"modifying vtree (swap node 7) (limit growth by {limit:.1f}x) ... ", end="")
  succeeded = manager_vtree.swap(manager, 1)  # limited
  print("succeeded!" if succeeded == 1 else "did not succeed!")
  print(f"  sdd size = {alpha.size()}")

  print("modifying vtree (swap node 7) (no limit) ... ", end="")
  succeeded = manager_vtree.swap(manager, 0)  # not limited
  print("succeeded!" if succeeded == 1 else "did not succeed!")
  print(f"  sdd size = {alpha.size()}")

  print("updating baseline of size limit ...")
  manager.update_vtree_size_limit()

  left_vtree = manager_vtree.left()
  limit = 1.2
  manager.set_vtree_operation_size_limit(limit)
  print(f"modifying vtree (swap node 5) (limit growth by {limit}x) ... ", end="")
  succeeded = left_vtree.swap(manager, 1)  # limited
  print("succeeded!" if succeeded == 1 else "did not succeed!")
  print(f"  sdd size = {alpha.size()}")

  limit = 1.3
  manager.set_vtree_operation_size_limit(limit)
  print(f"modifying vtree (swap node 5) (limit growth by {limit}x) ... ", end="")
  succeeded = left_vtree.swap(manager, 1)  # limited
  print("succeeded!" if succeeded == 1 else "did not succeed!")
  print(f"  sdd size = {alpha.size()}")

  # deref alpha, since ref's are no longer needed
  alpha.deref()

if __name__ == "__main__":
    main()