constructNetwork :: Set Predicate -> [Predicate] -> [Term] -> MLN -> UNetwork Predicate
constructNetwork query evidence ts (MLN m) = Set.foldr' (\p acc -> Map.insert p (mb p) acc) Map.empty ps
  where
    -- All groundings from all formulas in the knowledge base:
    gs = Set.foldr' (\g acc -> Set.union (FOL.groundings ts g) acc) Set.empty (Map.keysSet m)
    -- Predicates in the network
    ps = step query query
    -- The Markov blanket of predicate 'p', that is: all its neighbours.
    mb p = Set.delete p $ FS.allPredicates $ Set.filter (FOL.hasPred p) gs
    -- One step of the algorithm
    step f g
      | Set.null f = g
      | Set.findMin f `elem` evidence = step (Set.deleteMin f) g
      | otherwise =
        let mbq = mb $ Set.findMin f in
        step
          (Set.union (Set.deleteMin f) (Set.intersection mbq g))
          (Set.union g mbq)

-- | Builds a weighted knowledge base from a list of strings. If
-- 'Sphinx.Parser.parseWFOL' fails to parse a formula, it is ignored.
constructNetwork :: Set Predicate -> [Predicate] -> [Term] -> MLN -> UNetwork Predicate
constructNetwork query evidence ts (MLN m) = Set.foldr' (\p acc -> Map.insert p (mb p) acc) Map.empty ps
  where
    -- All groundings from all formulas in the knowledge base:
    gs = Set.foldr' (\g acc -> Set.union (FOL.groundings ts g) acc) Set.empty (Map.keysSet m)
    -- Predicates in the network
    ps = step query query
    -- The Markov blanket of predicate 'p', that is: all its neighbours.
    mb p = Set.delete p $ FS.allPredicates $ Set.filter (FOL.hasPred p) gs
    -- One step of the algorithm
    step f g
      | Set.null f = g
      | Set.findMin f `elem` evidence = step (Set.deleteMin f) g
      | otherwise =
        let mbq = mb $ Set.findMin f in
        step
          (Set.union (Set.deleteMin f) (Set.intersection mbq g))
          (Set.union g mbq)

-- | Builds a weighted knowledge base from a list of strings. If
-- 'Sphinx.Parser.parseWFOL' fails to parse a formula, it is ignored.
constructNetwork query evidence ts (MLN m) = Set.foldr' (\p acc -> Map.insert p (mb p) acc) Map.empty ps
  where
    -- All groundings from all formulas in the knowledge base:
    gs = Set.foldr' (\g acc -> Set.union (FOL.groundings ts g) acc) Set.empty (Map.keysSet m)
    -- Predicates in the network
    ps = step query query
    -- The Markov blanket of predicate 'p', that is: all its neighbours.
    mb p = Set.delete p $ FS.allPredicates $ Set.filter (FOL.hasPred p) gs
    -- One step of the algorithm
    step f g
      | Set.null f = g
      | Set.findMin f `elem` evidence = step (Set.deleteMin f) g
      | otherwise =
        let mbq = mb $ Set.findMin f in
        step
          (Set.union (Set.deleteMin f) (Set.intersection mbq g))
          (Set.union g mbq)

-- | Builds a weighted knowledge base from a list of strings. If
-- 'Sphinx.Parser.parseWFOL' fails to parse a formula, it is ignored.