deepxube.logic.logic_utils

  1from typing import List, Set, Tuple, Dict
  2from deepxube.logic.logic_objects import Clause, Literal, Atom
  3from deepxube.utils import misc_utils
  4import re
  5
  6
  7def parse_literal(lit_str: str) -> Literal:
  8    # check for negation
  9    not_match = re.match(r"\s*not\s+(.*)", lit_str)
 10    positive: bool
 11    if not_match is None:
 12        positive = True
 13    else:
 14        positive = False
 15        lit_str = not_match.group(1).strip()
 16
 17    # parse literal
 18    lit_str = misc_utils.remove_all_whitespace(lit_str)
 19    match = re.match(r"([^(]+)(\((.*)\))?", lit_str)
 20    assert match is not None
 21    pred_name: str = match.group(1)
 22    pred_args: Tuple[str, ...]
 23    if match.group(3) is not None:
 24        pred_args = tuple([x.strip() for x in match.group(3).split(",")])
 25    else:
 26        pred_args = tuple()
 27
 28    # TODO, better way to handle directions?
 29    directions = ["in"] * len(pred_args)
 30
 31    literal: Literal = Literal(pred_name, pred_args, tuple(directions), positive=positive)
 32    return literal
 33
 34
 35def replace_anon_vars(lit: Literal, all_lits: List[Literal]) -> Literal:
 36    all_args: Set[str] = set()
 37    for lit2 in [lit] + all_lits:
 38        all_args.update(lit2.arguments)
 39
 40    args_new: List[str] = []
 41    for arg in lit.arguments:
 42        if arg == "_":
 43            arg_new: str = "V0"
 44            arg_new_idx: int = 0
 45            while arg_new in all_args:
 46                arg_new_idx += 1
 47                arg_new = f"V{arg_new_idx}"
 48            all_args.add(arg_new)
 49
 50            args_new.append(arg_new)
 51        else:
 52            args_new.append(arg)
 53
 54    lit_ret: Literal = Literal(lit.predicate, tuple(args_new), lit.directions, positive=lit.positive)
 55    return lit_ret
 56
 57
 58def parse_clause(constr_str: str) -> Tuple[Clause, Dict[str, List[str]]]:
 59    head_str, body_str = constr_str.split(":-")
 60    head_lit = parse_literal(head_str)
 61
 62    # lits
 63    body_lit_strs: Tuple[str, ...] = tuple(re.findall(r"[^,]+\([^)]+\)|^\s*[^,]+|[^,)]+\s*$", body_str))
 64    body_lits: List[Literal] = []
 65    for body_lit_str in body_lit_strs:
 66        body_lit = parse_literal(body_lit_str)
 67        body_lits.append(body_lit)
 68
 69    # subs forbid
 70    subs_forbid: Dict[str, List[str]] = dict()
 71    sub_forbid_strs = re.findall(r"\s*([^,]+)\s*!\s*=\s*([^,]+)\s*", body_str)
 72    for v1, v2 in sub_forbid_strs:
 73        v1 = v1.strip()
 74        v2 = v2.strip()
 75        if v1 not in subs_forbid:
 76            subs_forbid[v1] = []
 77        if v2 not in subs_forbid:
 78            subs_forbid[v2] = []
 79
 80        subs_forbid[v1].append(v2)
 81        subs_forbid[v2].append(v1)
 82
 83    head_lit = replace_anon_vars(head_lit, [head_lit] + body_lits)
 84    for idx, body_lit in enumerate(body_lits):
 85        body_lits[idx] = replace_anon_vars(body_lit, [head_lit] + body_lits)
 86
 87    clause: Clause = Clause(head_lit, tuple(body_lits))
 88    return clause, subs_forbid
 89
 90
 91def copy_clause_with_new_head(clause: Clause, head_name_new: str) -> Clause:
 92    assert clause.head.positive, "Head should be positive"
 93    clause_new = Clause(Literal(head_name_new, clause.head.arguments, clause.head.directions,
 94                                positive=clause.head.positive), clause.body)
 95
 96    return clause_new
 97
 98
 99def atom_to_str(atom: Atom) -> str:
100    return f"{atom[0]}({','.join(atom[1:])})"
def parse_literal(lit_str: str) -> deepxube.logic.logic_objects.Literal:
 8def parse_literal(lit_str: str) -> Literal:
 9    # check for negation
10    not_match = re.match(r"\s*not\s+(.*)", lit_str)
11    positive: bool
12    if not_match is None:
13        positive = True
14    else:
15        positive = False
16        lit_str = not_match.group(1).strip()
17
18    # parse literal
19    lit_str = misc_utils.remove_all_whitespace(lit_str)
20    match = re.match(r"([^(]+)(\((.*)\))?", lit_str)
21    assert match is not None
22    pred_name: str = match.group(1)
23    pred_args: Tuple[str, ...]
24    if match.group(3) is not None:
25        pred_args = tuple([x.strip() for x in match.group(3).split(",")])
26    else:
27        pred_args = tuple()
28
29    # TODO, better way to handle directions?
30    directions = ["in"] * len(pred_args)
31
32    literal: Literal = Literal(pred_name, pred_args, tuple(directions), positive=positive)
33    return literal
36def replace_anon_vars(lit: Literal, all_lits: List[Literal]) -> Literal:
37    all_args: Set[str] = set()
38    for lit2 in [lit] + all_lits:
39        all_args.update(lit2.arguments)
40
41    args_new: List[str] = []
42    for arg in lit.arguments:
43        if arg == "_":
44            arg_new: str = "V0"
45            arg_new_idx: int = 0
46            while arg_new in all_args:
47                arg_new_idx += 1
48                arg_new = f"V{arg_new_idx}"
49            all_args.add(arg_new)
50
51            args_new.append(arg_new)
52        else:
53            args_new.append(arg)
54
55    lit_ret: Literal = Literal(lit.predicate, tuple(args_new), lit.directions, positive=lit.positive)
56    return lit_ret
def parse_clause( constr_str: str) -> Tuple[deepxube.logic.logic_objects.Clause, Dict[str, List[str]]]:
59def parse_clause(constr_str: str) -> Tuple[Clause, Dict[str, List[str]]]:
60    head_str, body_str = constr_str.split(":-")
61    head_lit = parse_literal(head_str)
62
63    # lits
64    body_lit_strs: Tuple[str, ...] = tuple(re.findall(r"[^,]+\([^)]+\)|^\s*[^,]+|[^,)]+\s*$", body_str))
65    body_lits: List[Literal] = []
66    for body_lit_str in body_lit_strs:
67        body_lit = parse_literal(body_lit_str)
68        body_lits.append(body_lit)
69
70    # subs forbid
71    subs_forbid: Dict[str, List[str]] = dict()
72    sub_forbid_strs = re.findall(r"\s*([^,]+)\s*!\s*=\s*([^,]+)\s*", body_str)
73    for v1, v2 in sub_forbid_strs:
74        v1 = v1.strip()
75        v2 = v2.strip()
76        if v1 not in subs_forbid:
77            subs_forbid[v1] = []
78        if v2 not in subs_forbid:
79            subs_forbid[v2] = []
80
81        subs_forbid[v1].append(v2)
82        subs_forbid[v2].append(v1)
83
84    head_lit = replace_anon_vars(head_lit, [head_lit] + body_lits)
85    for idx, body_lit in enumerate(body_lits):
86        body_lits[idx] = replace_anon_vars(body_lit, [head_lit] + body_lits)
87
88    clause: Clause = Clause(head_lit, tuple(body_lits))
89    return clause, subs_forbid
def copy_clause_with_new_head( clause: deepxube.logic.logic_objects.Clause, head_name_new: str) -> deepxube.logic.logic_objects.Clause:
92def copy_clause_with_new_head(clause: Clause, head_name_new: str) -> Clause:
93    assert clause.head.positive, "Head should be positive"
94    clause_new = Clause(Literal(head_name_new, clause.head.arguments, clause.head.directions,
95                                positive=clause.head.positive), clause.body)
96
97    return clause_new
def atom_to_str(atom: Tuple[str, ...]) -> str:
100def atom_to_str(atom: Atom) -> str:
101    return f"{atom[0]}({','.join(atom[1:])})"