Zulip Chat Archive

Stream: general

Topic: the asymptotics of victor taelins sat solver


Jared green (Feb 04 2025 at 19:03):

is victor taelin still interested in determining the time complexity of his SAT solver for hvm? i wrote it in lean with an optimization (i wont try to prove termination):

-- i converted the sat solver algorithm using superpositions
--from https://gist.github.com/VictorTaelin/9061306220929f04e7e6980f23ade615
--(as i understand it) to the lambda calculus
--this algorithm is exactly DPLL with implicit unit propagation

--the input variables are given numbers so that splits are sorted.
inductive superposition
  where
  | just : Bool -> superposition
  | split : Nat -> Bool -> (superposition × superposition) -> superposition


namespace superposition

def seq (s1 s2 : superposition) : Bool :=
  match s1, s2 with
  | just a, just b => a = b
  | split n a (b, c),split m d (e, f) => n == m &&
    if a = d
    then seq b e && seq c f
    else seq b f && seq c e
  | _, _ => false

--a superposition of identical branches collapses to one
def converge (s : superposition) : superposition :=
  match s with
  | just a => just a
  | split n a (b, c) => match (converge b, converge c) with
    | (b, c) => if seq b c then b else split n a (b, c)


--these will place branches that can have true on them on the left side
def not  (s : superposition ) : superposition :=
  match converge s with
  | just a => just !a
  | split n a (b, c) => split n (!a) (not c, not b)

def and (s1 s2 : superposition) : superposition :=
  match converge s1, converge s2 with
  | just a, just b => just (a && b)
  | just false, _ => just false
  | just true,  t => t
  | _, just false => just false
  | t, just true => t
  | split n a (b, c), split m d (e, f) =>
    if n == m then
      if a = d then  split n a (and b e, and c f)
      else match converge (and b f), converge (and c e) with
      | just true, g => split n a (just true, g)
      | g, just true => split n (!a) (just true, g)
      | just false, g => split n (!a) (g, just false)
      | g, just false => split n a (g, just false)
      | g, h => if a then split n true (g, h) else split n true (h, g) --this makes the same match statement happen less
    else if n < m then split n a (and b s2, and c s2)
    else split m d (and e s1, and f s1)

def or (s1 s2 : superposition) : superposition :=
  match converge s1, converge s2 with
  | just a, just b => just (a || b)
  | just false, t => t
  | just true, _ => just true
  | t, just false => t
  | _, just true => just true
  | split n a (b, c), split m d (e, f) =>
    if n == m then
      if a = d then  split n a (or b e, or c f)
      else match converge (or b f), converge (or c e) with
      | just true, g => split n a (just true, g)
      | g, just true => split n (!a) (just true, g)
      | just false, g => split n (!a) (g, just false)
      | g, just false => split n a (g, just false)
      | g, h => if a then split n true (g, h) else split n true (h, g)
    else if n < m then split n a (or b s2, or c s2)
    else split m d (or e s1, or f s1)


--this is to get the negation normal form before attempting to find a solution,
--avoiding flipping the superpostions more than is necessary
inductive circuit
  where
  | vari : Nat -> circuit
  | Not : circuit -> circuit
  | And : circuit -> circuit -> circuit
  | Or : circuit -> circuit -> circuit

def normalize (c : circuit) : circuit :=
  match c with
  | circuit.vari n => circuit.vari n
  | circuit.Not (circuit.Not a) => normalize a
  | circuit.Not (circuit.vari n) => circuit.Not (circuit.vari n)
  | circuit.Not (circuit.And a b) => circuit.Or (normalize (circuit.Not a)) (normalize (circuit.Not b))
  | circuit.And a b => circuit.And (normalize a) (normalize b)
  | circuit.Not (circuit.Or a b) => circuit.And (normalize (circuit.Not a)) (normalize (circuit.Not b))
  | circuit.Or a b => circuit.Or (normalize a) (normalize b)

def tosuperposition (c : circuit) : superposition :=
  match normalize c with
  | circuit.vari n => split n true (just true,just false)
  | circuit.Not a => not (tosuperposition a)
  | circuit.And a b => and (tosuperposition a) (tosuperposition b)
  | circuit.Or a b => or (tosuperposition a) (tosuperposition b)

--gets the left brach. if there is a solution, the left branch is one
def leftpath (s : superposition) : List (Nat × Bool) :=
  match s with
  | just _ => []
  | split n a (b, _) => (n, a) :: leftpath b

def firstsolution (c : circuit) : Option (List (Nat × Bool)) :=
  match converge (tosuperposition c) with
  | just false => none
  | a => some (leftpath a)

def satisfiable (c : circuit) : Bool :=
  firstsolution c != none

Last updated: May 02 2025 at 03:31 UTC