diff --git a/pyreason/scripts/interpretation/interpretation.py b/pyreason/scripts/interpretation/interpretation.py index 1e0cc4e..57777b1 100755 --- a/pyreason/scripts/interpretation/interpretation.py +++ b/pyreason/scripts/interpretation/interpretation.py @@ -1020,7 +1020,7 @@ def _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map else: if (g1, g2) in edges_set: valid_edge_groundings.append((g1, g2)) - + # Loop through the head variable groundings for valid_e in valid_edge_groundings: head_var_1_grounding, head_var_2_grounding = valid_e[0], valid_e[1] @@ -1195,503 +1195,6 @@ def check_all_clause_satisfaction(interpretations_node, interpretations_edge, cl return satisfaction -@numba.njit(cache=True) -def _ground_node_rule(rule, interpretations_node, interpretations_edge, nodes, neighbors, reverse_neighbors, atom_trace, reverse_graph, nodes_to_skip): - # Extract rule params - rule_type = rule.get_type() - clauses = rule.get_clauses() - thresholds = rule.get_thresholds() - ann_fn = rule.get_annotation_function() - rule_edges = rule.get_edges() - - # We return a list of tuples which specify the target nodes/edges that have made the rule body true - applicable_rules = numba.typed.List.empty_list(node_applicable_rule_type) - - # Create pre-allocated data structure so that parallel code does not need to use "append" to be threadsafe - # One array for each node, then condense into a single list later - applicable_rules_threadsafe = numba.typed.List([numba.typed.List.empty_list(node_applicable_rule_type) for _ in nodes]) - - # Return empty list if rule is not node rule and if we are not inferring edges - if rule_type != 'node' and rule_edges[0] == '': - return applicable_rules - - # Steps - # 1. Loop through all nodes and evaluate each clause with that node and check the truth with the thresholds - # 2. Inside the clause loop it may be necessary to loop through all nodes/edges while grounding the variables - # 3. If the clause is true add the qualified nodes and qualified edges to the atom trace, if on. Break otherwise - # 4. After going through all clauses, add to the annotations list all the annotations of the specified subset. These will be passed to the annotation function - # 5. Finally, if there are any edges to be added, place them in the list - - for piter in prange(len(nodes)): - target_node = nodes[piter] - if target_node in nodes_to_skip: - continue - # Initialize dictionary where keys are strings (x1, x2 etc.) and values are lists of qualified neighbors - # Keep track of qualified nodes and qualified edges - # If it's a node clause update (x1 or x2 etc.) qualified neighbors, if it's an edge clause update the qualified neighbors for the source and target (x1, x2) - subsets = numba.typed.Dict.empty(key_type=numba.types.string, value_type=list_of_nodes) - qualified_nodes = numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)) - qualified_edges = numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)) - annotations = numba.typed.List.empty_list(numba.typed.List.empty_list(interval.interval_type)) - edges_to_be_added = (numba.typed.List.empty_list(node_type), numba.typed.List.empty_list(node_type), rule_edges[-1]) - clause_type_and_variables = numba.typed.List.empty_list(clause_data) - - satisfaction = True - for i, clause in enumerate(clauses): - # Unpack clause variables - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - clause_bnd = clause[3] - clause_operator = clause[4] - - # This is a node clause - # The groundings for node clauses are either the target node, neighbors of the target node, or an existing subset of nodes - if clause_type == 'node': - clause_var_1 = clause_variables[0] - subset = get_node_rule_node_clause_subset(clause_var_1, target_node, subsets, nodes) - - subsets[clause_var_1] = get_qualified_components_node_clause(interpretations_node, subset, clause_label, clause_bnd) - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node', clause_label, numba.typed.List([clause_var_1]))) - - # This is an edge clause - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_source, subset_target = get_node_rule_edge_clause_subset(clause_var_1, clause_var_2, target_node, subsets, neighbors, reverse_neighbors, nodes) - - # Get qualified edges - qe = get_qualified_components_edge_clause(interpretations_edge, subset_source, subset_target, clause_label, clause_bnd, reverse_graph) - subsets[clause_var_1] = qe[0] - subsets[clause_var_2] = qe[1] - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - else: - # This is a comparison clause - # Make sure there is at least one ground atom such that pred-num(x) : [1,1] or pred-num(x,y) : [1,1] - # Remember that the predicate in the clause will not contain the "-num" where num is some number. - # We have to remove that manually while checking - # Steps: - # 1. get qualified nodes/edges as well as number associated for first predicate - # 2. get qualified nodes/edges as well as number associated for second predicate - # 3. if there's no number in steps 1 or 2 return false clause - # 4. do comparison with each qualified component from step 1 with each qualified component in step 2 - - # It's a node comparison - if len(clause_variables) == 2: - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_1 = get_node_rule_node_clause_subset(clause_var_1, target_node, subsets, nodes) - subset_2 = get_node_rule_node_clause_subset(clause_var_2, target_node, subsets, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1, numbers_1 = get_qualified_components_node_comparison_clause(interpretations_node, subset_1, clause_label, clause_bnd) - qualified_nodes_for_comparison_2, numbers_2 = get_qualified_components_node_comparison_clause(interpretations_node, subset_2, clause_label, clause_bnd) - - # It's an edge comparison - elif len(clause_variables) == 4: - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables[0], clause_variables[1], clause_variables[2], clause_variables[3] - subset_1_source, subset_1_target = get_node_rule_edge_clause_subset(clause_var_1_source, clause_var_1_target, target_node, subsets, neighbors, reverse_neighbors, nodes) - subset_2_source, subset_2_target = get_node_rule_edge_clause_subset(clause_var_2_source, clause_var_2_target, target_node, subsets, neighbors, reverse_neighbors, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1_source, qualified_nodes_for_comparison_1_target, numbers_1 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_1_source, subset_1_target, clause_label, clause_bnd, reverse_graph) - qualified_nodes_for_comparison_2_source, qualified_nodes_for_comparison_2_target, numbers_2 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_2_source, subset_2_target, clause_label, clause_bnd, reverse_graph) - - # Check if thresholds are satisfied - # If it's a comparison clause we just need to check if the numbers list is not empty (no threshold support) - if clause_type == 'comparison': - if len(numbers_1) == 0 or len(numbers_2) == 0: - satisfaction = False - # Node comparison. Compare stage - elif len(clause_variables) == 2: - satisfaction, qualified_nodes_1, qualified_nodes_2 = compare_numbers_node_predicate(numbers_1, numbers_2, clause_operator, qualified_nodes_for_comparison_1, qualified_nodes_for_comparison_2) - - # Update subsets with final qualified nodes - subsets[clause_var_1] = qualified_nodes_1 - subsets[clause_var_2] = qualified_nodes_2 - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node-comparison', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - # Edge comparison. Compare stage - else: - satisfaction, qualified_nodes_1_source, qualified_nodes_1_target, qualified_nodes_2_source, qualified_nodes_2_target = compare_numbers_edge_predicate(numbers_1, numbers_2, clause_operator, - qualified_nodes_for_comparison_1_source, - qualified_nodes_for_comparison_1_target, - qualified_nodes_for_comparison_2_source, - qualified_nodes_for_comparison_2_target) - # Update subsets with final qualified nodes - subsets[clause_var_1_source] = qualified_nodes_1_source - subsets[clause_var_1_target] = qualified_nodes_1_target - subsets[clause_var_2_source] = qualified_nodes_2_source - subsets[clause_var_2_target] = qualified_nodes_2_target - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge-comparison', clause_label, numba.typed.List([clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target]))) - - # Non comparison clause - else: - if clause_type == 'node': - satisfaction = check_node_clause_satisfaction(interpretations_node, subsets, subset, clause_var_1, clause_label, thresholds[i]) and satisfaction - else: - satisfaction = check_edge_clause_satisfaction(interpretations_edge, subsets, subset_source, subset_target, clause_var_1, clause_label, thresholds[i], reverse_graph) and satisfaction - - # Refine subsets based on any updates - if satisfaction: - satisfaction = refine_subsets_node_rule(interpretations_edge, clauses, i, subsets, target_node, neighbors, reverse_neighbors, nodes, thresholds, reverse_graph) and satisfaction - - # Exit loop if even one clause is not satisfied - if not satisfaction: - break - - if satisfaction: - # Collect edges to be added - source, target, _ = rule_edges - - # Edges to be added - if source != '' and target != '': - # Check if edge nodes are target - if source == '__target': - edges_to_be_added[0].append(target_node) - elif source in subsets: - edges_to_be_added[0].extend(subsets[source]) - else: - edges_to_be_added[0].append(source) - - if target == '__target': - edges_to_be_added[1].append(target_node) - elif target in subsets: - edges_to_be_added[1].extend(subsets[target]) - else: - edges_to_be_added[1].append(target) - - # Loop through the clause data and setup final annotations and trace variables - # 1. Add qualified nodes/edges to trace - # 2. Add annotations to annotation function variable - for i, clause in enumerate(clause_type_and_variables): - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - - if clause_type == 'node': - clause_var_1 = clause_variables[0] - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List(subsets[clause_var_1])) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in subsets[clause_var_1]: - a.append(interpretations_node[qn].world[clause_label]) - annotations.append(a) - - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2]))) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2])): - a.append(interpretations_edge[qe].world[clause_label]) - annotations.append(a) - - elif clause_type == 'node-comparison': - clause_var_1, clause_var_2 = clause_variables - qualified_nodes_1 = subsets[clause_var_1] - qualified_nodes_2 = subsets[clause_var_2] - qualified_comparison_nodes = numba.typed.List(qualified_nodes_1) - qualified_comparison_nodes.extend(qualified_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(qualified_comparison_nodes) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - elif clause_type == 'edge-comparison': - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables - qualified_nodes_1_source = subsets[clause_var_1_source] - qualified_nodes_1_target = subsets[clause_var_1_target] - qualified_nodes_2_source = subsets[clause_var_2_source] - qualified_nodes_2_target = subsets[clause_var_2_target] - qualified_comparison_nodes_1 = numba.typed.List(zip(qualified_nodes_1_source, qualified_nodes_1_target)) - qualified_comparison_nodes_2 = numba.typed.List(zip(qualified_nodes_2_source, qualified_nodes_2_target)) - qualified_comparison_nodes = numba.typed.List(qualified_comparison_nodes_1) - qualified_comparison_nodes.extend(qualified_comparison_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(qualified_comparison_nodes) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - # node/edge, annotations, qualified nodes, qualified edges, edges to be added - applicable_rules_threadsafe[piter] = numba.typed.List([(target_node, annotations, qualified_nodes, qualified_edges, edges_to_be_added)]) - - # Merge all threadsafe rules into one single array - for applicable_rule in applicable_rules_threadsafe: - if len(applicable_rule) > 0: - applicable_rules.append(applicable_rule[0]) - - return applicable_rules - - -@numba.njit(cache=True) -def _ground_edge_rule(rule, interpretations_node, interpretations_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, reverse_graph, edges_to_skip): - # Extract rule params - rule_type = rule.get_type() - clauses = rule.get_clauses() - thresholds = rule.get_thresholds() - ann_fn = rule.get_annotation_function() - rule_edges = rule.get_edges() - - # We return a list of tuples which specify the target nodes/edges that have made the rule body true - applicable_rules = numba.typed.List.empty_list(edge_applicable_rule_type) - - # Create pre-allocated data structure so that parallel code does not need to use "append" to be threadsafe - # One array for each node, then condense into a single list later - applicable_rules_threadsafe = numba.typed.List([numba.typed.List.empty_list(edge_applicable_rule_type) for _ in edges]) - - # Return empty list if rule is not node rule - if rule_type != 'edge': - return applicable_rules - - # Steps - # 1. Loop through all nodes and evaluate each clause with that node and check the truth with the thresholds - # 2. Inside the clause loop it may be necessary to loop through all nodes/edges while grounding the variables - # 3. If the clause is true add the qualified nodes and qualified edges to the atom trace, if on. Break otherwise - # 4. After going through all clauses, add to the annotations list all the annotations of the specified subset. These will be passed to the annotation function - # 5. Finally, if there are any edges to be added, place them in the list - - for piter in prange(len(edges)): - target_edge = edges[piter] - if target_edge in edges_to_skip: - continue - # Initialize dictionary where keys are strings (x1, x2 etc.) and values are lists of qualified neighbors - # Keep track of qualified nodes and qualified edges - # If it's a node clause update (x1 or x2 etc.) qualified neighbors, if it's an edge clause update the qualified neighbors for the source and target (x1, x2) - subsets = numba.typed.Dict.empty(key_type=numba.types.string, value_type=list_of_nodes) - qualified_nodes = numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)) - qualified_edges = numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)) - annotations = numba.typed.List.empty_list(numba.typed.List.empty_list(interval.interval_type)) - edges_to_be_added = (numba.typed.List.empty_list(node_type), numba.typed.List.empty_list(node_type), rule_edges[-1]) - clause_type_and_variables = numba.typed.List.empty_list(clause_data) - - satisfaction = True - for i, clause in enumerate(clauses): - # Unpack clause variables - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - clause_bnd = clause[3] - clause_operator = clause[4] - - # This is a node clause - # The groundings for node clauses are either the source, target, neighbors of the source node, or an existing subset of nodes - if clause_type == 'node': - clause_var_1 = clause_variables[0] - subset = get_edge_rule_node_clause_subset(clause_var_1, target_edge, subsets, nodes) - - subsets[clause_var_1] = get_qualified_components_node_clause(interpretations_node, subset, clause_label, clause_bnd) - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node', clause_label, numba.typed.List([clause_var_1]))) - - # This is an edge clause - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_source, subset_target = get_edge_rule_edge_clause_subset(clause_var_1, clause_var_2, target_edge, subsets, neighbors, reverse_neighbors, nodes) - - # Get qualified edges - qe = get_qualified_components_edge_clause(interpretations_edge, subset_source, subset_target, clause_label, clause_bnd, reverse_graph) - subsets[clause_var_1] = qe[0] - subsets[clause_var_2] = qe[1] - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - else: - # This is a comparison clause - # Make sure there is at least one ground atom such that pred-num(x) : [1,1] or pred-num(x,y) : [1,1] - # Remember that the predicate in the clause will not contain the "-num" where num is some number. - # We have to remove that manually while checking - # Steps: - # 1. get qualified nodes/edges as well as number associated for first predicate - # 2. get qualified nodes/edges as well as number associated for second predicate - # 3. if there's no number in steps 1 or 2 return false clause - # 4. do comparison with each qualified component from step 1 with each qualified component in step 2 - - # It's a node comparison - if len(clause_variables) == 2: - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_1 = get_edge_rule_node_clause_subset(clause_var_1, target_edge, subsets, nodes) - subset_2 = get_edge_rule_node_clause_subset(clause_var_2, target_edge, subsets, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1, numbers_1 = get_qualified_components_node_comparison_clause(interpretations_node, subset_1, clause_label, clause_bnd) - qualified_nodes_for_comparison_2, numbers_2 = get_qualified_components_node_comparison_clause(interpretations_node, subset_2, clause_label, clause_bnd) - - # It's an edge comparison - elif len(clause_variables) == 4: - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables[0], clause_variables[1], clause_variables[2], clause_variables[3] - subset_1_source, subset_1_target = get_edge_rule_edge_clause_subset(clause_var_1_source, clause_var_1_target, target_edge, subsets, neighbors, reverse_neighbors, nodes) - subset_2_source, subset_2_target = get_edge_rule_edge_clause_subset(clause_var_2_source, clause_var_2_target, target_edge, subsets, neighbors, reverse_neighbors, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1_source, qualified_nodes_for_comparison_1_target, numbers_1 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_1_source, subset_1_target, clause_label, clause_bnd, reverse_graph) - qualified_nodes_for_comparison_2_source, qualified_nodes_for_comparison_2_target, numbers_2 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_2_source, subset_2_target, clause_label, clause_bnd, reverse_graph) - - # Check if thresholds are satisfied - # If it's a comparison clause we just need to check if the numbers list is not empty (no threshold support) - if clause_type == 'comparison': - if len(numbers_1) == 0 or len(numbers_2) == 0: - satisfaction = False - # Node comparison. Compare stage - elif len(clause_variables) == 2: - satisfaction, qualified_nodes_1, qualified_nodes_2 = compare_numbers_node_predicate(numbers_1, numbers_2, clause_operator, qualified_nodes_for_comparison_1, qualified_nodes_for_comparison_2) - - # Update subsets with final qualified nodes - subsets[clause_var_1] = qualified_nodes_1 - subsets[clause_var_2] = qualified_nodes_2 - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node-comparison', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - # Edge comparison. Compare stage - else: - satisfaction, qualified_nodes_1_source, qualified_nodes_1_target, qualified_nodes_2_source, qualified_nodes_2_target = compare_numbers_edge_predicate(numbers_1, numbers_2, clause_operator, - qualified_nodes_for_comparison_1_source, - qualified_nodes_for_comparison_1_target, - qualified_nodes_for_comparison_2_source, - qualified_nodes_for_comparison_2_target) - # Update subsets with final qualified nodes - subsets[clause_var_1_source] = qualified_nodes_1_source - subsets[clause_var_1_target] = qualified_nodes_1_target - subsets[clause_var_2_source] = qualified_nodes_2_source - subsets[clause_var_2_target] = qualified_nodes_2_target - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge-comparison', clause_label, numba.typed.List([clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target]))) - - # Non comparison clause - else: - if clause_type == 'node': - satisfaction = check_node_clause_satisfaction(interpretations_node, subsets, subset, clause_var_1, clause_label, thresholds[i]) and satisfaction - else: - satisfaction = check_edge_clause_satisfaction(interpretations_edge, subsets, subset_source, subset_target, clause_var_1, clause_label, thresholds[i], reverse_graph) and satisfaction - - # Refine subsets based on any updates - if satisfaction: - satisfaction = refine_subsets_edge_rule(interpretations_edge, clauses, i, subsets, target_edge, neighbors, reverse_neighbors, nodes, thresholds, reverse_graph) and satisfaction - - # Exit loop if even one clause is not satisfied - if not satisfaction: - break - - # Here we are done going through each clause of the rule - # If all clauses we're satisfied, proceed to collect annotations and prepare edges to be added - if satisfaction: - # Loop through the clause data and setup final annotations and trace variables - # 1. Add qualified nodes/edges to trace - # 2. Add annotations to annotation function variable - for i, clause in enumerate(clause_type_and_variables): - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - - if clause_type == 'node': - clause_var_1 = clause_variables[0] - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List(subsets[clause_var_1])) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in subsets[clause_var_1]: - a.append(interpretations_node[qn].world[clause_label]) - annotations.append(a) - - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2]))) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2])): - a.append(interpretations_edge[qe].world[clause_label]) - annotations.append(a) - - elif clause_type == 'node-comparison': - clause_var_1, clause_var_2 = clause_variables - qualified_nodes_1 = subsets[clause_var_1] - qualified_nodes_2 = subsets[clause_var_2] - qualified_comparison_nodes = numba.typed.List(qualified_nodes_1) - qualified_comparison_nodes.extend(qualified_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(qualified_comparison_nodes) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - elif clause_type == 'edge-comparison': - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables - qualified_nodes_1_source = subsets[clause_var_1_source] - qualified_nodes_1_target = subsets[clause_var_1_target] - qualified_nodes_2_source = subsets[clause_var_2_source] - qualified_nodes_2_target = subsets[clause_var_2_target] - qualified_comparison_nodes_1 = numba.typed.List(zip(qualified_nodes_1_source, qualified_nodes_1_target)) - qualified_comparison_nodes_2 = numba.typed.List(zip(qualified_nodes_2_source, qualified_nodes_2_target)) - qualified_comparison_nodes = numba.typed.List(qualified_comparison_nodes_1) - qualified_comparison_nodes.extend(qualified_comparison_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(qualified_comparison_nodes) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - # node/edge, annotations, qualified nodes, qualified edges, edges to be added - applicable_rules_threadsafe[piter] = numba.typed.List([(target_edge, annotations, qualified_nodes, qualified_edges, edges_to_be_added)]) - - # Merge all threadsafe rules into one single array - for applicable_rule in applicable_rules_threadsafe: - if len(applicable_rule) > 0: - applicable_rules.append(applicable_rule[0]) - - return applicable_rules - - @numba.njit(cache=True) def refine_groundings(clause_variables, groundings, groundings_edges, dependency_graph_neighbors, dependency_graph_reverse_neighbors): # Loop through the dependency graph and refine the groundings that have connections diff --git a/pyreason/scripts/interpretation/interpretation_parallel.py b/pyreason/scripts/interpretation/interpretation_parallel.py index 577f375..77ac606 100644 --- a/pyreason/scripts/interpretation/interpretation_parallel.py +++ b/pyreason/scripts/interpretation/interpretation_parallel.py @@ -1020,7 +1020,7 @@ def _ground_rule(rule, interpretations_node, interpretations_edge, predicate_map else: if (g1, g2) in edges_set: valid_edge_groundings.append((g1, g2)) - + # Loop through the head variable groundings for valid_e in valid_edge_groundings: head_var_1_grounding, head_var_2_grounding = valid_e[0], valid_e[1] @@ -1195,503 +1195,6 @@ def check_all_clause_satisfaction(interpretations_node, interpretations_edge, cl return satisfaction -@numba.njit(cache=True) -def _ground_node_rule(rule, interpretations_node, interpretations_edge, nodes, neighbors, reverse_neighbors, atom_trace, reverse_graph, nodes_to_skip): - # Extract rule params - rule_type = rule.get_type() - clauses = rule.get_clauses() - thresholds = rule.get_thresholds() - ann_fn = rule.get_annotation_function() - rule_edges = rule.get_edges() - - # We return a list of tuples which specify the target nodes/edges that have made the rule body true - applicable_rules = numba.typed.List.empty_list(node_applicable_rule_type) - - # Create pre-allocated data structure so that parallel code does not need to use "append" to be threadsafe - # One array for each node, then condense into a single list later - applicable_rules_threadsafe = numba.typed.List([numba.typed.List.empty_list(node_applicable_rule_type) for _ in nodes]) - - # Return empty list if rule is not node rule and if we are not inferring edges - if rule_type != 'node' and rule_edges[0] == '': - return applicable_rules - - # Steps - # 1. Loop through all nodes and evaluate each clause with that node and check the truth with the thresholds - # 2. Inside the clause loop it may be necessary to loop through all nodes/edges while grounding the variables - # 3. If the clause is true add the qualified nodes and qualified edges to the atom trace, if on. Break otherwise - # 4. After going through all clauses, add to the annotations list all the annotations of the specified subset. These will be passed to the annotation function - # 5. Finally, if there are any edges to be added, place them in the list - - for piter in prange(len(nodes)): - target_node = nodes[piter] - if target_node in nodes_to_skip: - continue - # Initialize dictionary where keys are strings (x1, x2 etc.) and values are lists of qualified neighbors - # Keep track of qualified nodes and qualified edges - # If it's a node clause update (x1 or x2 etc.) qualified neighbors, if it's an edge clause update the qualified neighbors for the source and target (x1, x2) - subsets = numba.typed.Dict.empty(key_type=numba.types.string, value_type=list_of_nodes) - qualified_nodes = numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)) - qualified_edges = numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)) - annotations = numba.typed.List.empty_list(numba.typed.List.empty_list(interval.interval_type)) - edges_to_be_added = (numba.typed.List.empty_list(node_type), numba.typed.List.empty_list(node_type), rule_edges[-1]) - clause_type_and_variables = numba.typed.List.empty_list(clause_data) - - satisfaction = True - for i, clause in enumerate(clauses): - # Unpack clause variables - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - clause_bnd = clause[3] - clause_operator = clause[4] - - # This is a node clause - # The groundings for node clauses are either the target node, neighbors of the target node, or an existing subset of nodes - if clause_type == 'node': - clause_var_1 = clause_variables[0] - subset = get_node_rule_node_clause_subset(clause_var_1, target_node, subsets, nodes) - - subsets[clause_var_1] = get_qualified_components_node_clause(interpretations_node, subset, clause_label, clause_bnd) - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node', clause_label, numba.typed.List([clause_var_1]))) - - # This is an edge clause - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_source, subset_target = get_node_rule_edge_clause_subset(clause_var_1, clause_var_2, target_node, subsets, neighbors, reverse_neighbors, nodes) - - # Get qualified edges - qe = get_qualified_components_edge_clause(interpretations_edge, subset_source, subset_target, clause_label, clause_bnd, reverse_graph) - subsets[clause_var_1] = qe[0] - subsets[clause_var_2] = qe[1] - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - else: - # This is a comparison clause - # Make sure there is at least one ground atom such that pred-num(x) : [1,1] or pred-num(x,y) : [1,1] - # Remember that the predicate in the clause will not contain the "-num" where num is some number. - # We have to remove that manually while checking - # Steps: - # 1. get qualified nodes/edges as well as number associated for first predicate - # 2. get qualified nodes/edges as well as number associated for second predicate - # 3. if there's no number in steps 1 or 2 return false clause - # 4. do comparison with each qualified component from step 1 with each qualified component in step 2 - - # It's a node comparison - if len(clause_variables) == 2: - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_1 = get_node_rule_node_clause_subset(clause_var_1, target_node, subsets, nodes) - subset_2 = get_node_rule_node_clause_subset(clause_var_2, target_node, subsets, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1, numbers_1 = get_qualified_components_node_comparison_clause(interpretations_node, subset_1, clause_label, clause_bnd) - qualified_nodes_for_comparison_2, numbers_2 = get_qualified_components_node_comparison_clause(interpretations_node, subset_2, clause_label, clause_bnd) - - # It's an edge comparison - elif len(clause_variables) == 4: - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables[0], clause_variables[1], clause_variables[2], clause_variables[3] - subset_1_source, subset_1_target = get_node_rule_edge_clause_subset(clause_var_1_source, clause_var_1_target, target_node, subsets, neighbors, reverse_neighbors, nodes) - subset_2_source, subset_2_target = get_node_rule_edge_clause_subset(clause_var_2_source, clause_var_2_target, target_node, subsets, neighbors, reverse_neighbors, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1_source, qualified_nodes_for_comparison_1_target, numbers_1 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_1_source, subset_1_target, clause_label, clause_bnd, reverse_graph) - qualified_nodes_for_comparison_2_source, qualified_nodes_for_comparison_2_target, numbers_2 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_2_source, subset_2_target, clause_label, clause_bnd, reverse_graph) - - # Check if thresholds are satisfied - # If it's a comparison clause we just need to check if the numbers list is not empty (no threshold support) - if clause_type == 'comparison': - if len(numbers_1) == 0 or len(numbers_2) == 0: - satisfaction = False - # Node comparison. Compare stage - elif len(clause_variables) == 2: - satisfaction, qualified_nodes_1, qualified_nodes_2 = compare_numbers_node_predicate(numbers_1, numbers_2, clause_operator, qualified_nodes_for_comparison_1, qualified_nodes_for_comparison_2) - - # Update subsets with final qualified nodes - subsets[clause_var_1] = qualified_nodes_1 - subsets[clause_var_2] = qualified_nodes_2 - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node-comparison', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - # Edge comparison. Compare stage - else: - satisfaction, qualified_nodes_1_source, qualified_nodes_1_target, qualified_nodes_2_source, qualified_nodes_2_target = compare_numbers_edge_predicate(numbers_1, numbers_2, clause_operator, - qualified_nodes_for_comparison_1_source, - qualified_nodes_for_comparison_1_target, - qualified_nodes_for_comparison_2_source, - qualified_nodes_for_comparison_2_target) - # Update subsets with final qualified nodes - subsets[clause_var_1_source] = qualified_nodes_1_source - subsets[clause_var_1_target] = qualified_nodes_1_target - subsets[clause_var_2_source] = qualified_nodes_2_source - subsets[clause_var_2_target] = qualified_nodes_2_target - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge-comparison', clause_label, numba.typed.List([clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target]))) - - # Non comparison clause - else: - if clause_type == 'node': - satisfaction = check_node_clause_satisfaction(interpretations_node, subsets, subset, clause_var_1, clause_label, thresholds[i]) and satisfaction - else: - satisfaction = check_edge_clause_satisfaction(interpretations_edge, subsets, subset_source, subset_target, clause_var_1, clause_label, thresholds[i], reverse_graph) and satisfaction - - # Refine subsets based on any updates - if satisfaction: - satisfaction = refine_subsets_node_rule(interpretations_edge, clauses, i, subsets, target_node, neighbors, reverse_neighbors, nodes, thresholds, reverse_graph) and satisfaction - - # Exit loop if even one clause is not satisfied - if not satisfaction: - break - - if satisfaction: - # Collect edges to be added - source, target, _ = rule_edges - - # Edges to be added - if source != '' and target != '': - # Check if edge nodes are target - if source == '__target': - edges_to_be_added[0].append(target_node) - elif source in subsets: - edges_to_be_added[0].extend(subsets[source]) - else: - edges_to_be_added[0].append(source) - - if target == '__target': - edges_to_be_added[1].append(target_node) - elif target in subsets: - edges_to_be_added[1].extend(subsets[target]) - else: - edges_to_be_added[1].append(target) - - # Loop through the clause data and setup final annotations and trace variables - # 1. Add qualified nodes/edges to trace - # 2. Add annotations to annotation function variable - for i, clause in enumerate(clause_type_and_variables): - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - - if clause_type == 'node': - clause_var_1 = clause_variables[0] - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List(subsets[clause_var_1])) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in subsets[clause_var_1]: - a.append(interpretations_node[qn].world[clause_label]) - annotations.append(a) - - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2]))) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2])): - a.append(interpretations_edge[qe].world[clause_label]) - annotations.append(a) - - elif clause_type == 'node-comparison': - clause_var_1, clause_var_2 = clause_variables - qualified_nodes_1 = subsets[clause_var_1] - qualified_nodes_2 = subsets[clause_var_2] - qualified_comparison_nodes = numba.typed.List(qualified_nodes_1) - qualified_comparison_nodes.extend(qualified_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(qualified_comparison_nodes) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - elif clause_type == 'edge-comparison': - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables - qualified_nodes_1_source = subsets[clause_var_1_source] - qualified_nodes_1_target = subsets[clause_var_1_target] - qualified_nodes_2_source = subsets[clause_var_2_source] - qualified_nodes_2_target = subsets[clause_var_2_target] - qualified_comparison_nodes_1 = numba.typed.List(zip(qualified_nodes_1_source, qualified_nodes_1_target)) - qualified_comparison_nodes_2 = numba.typed.List(zip(qualified_nodes_2_source, qualified_nodes_2_target)) - qualified_comparison_nodes = numba.typed.List(qualified_comparison_nodes_1) - qualified_comparison_nodes.extend(qualified_comparison_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(qualified_comparison_nodes) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - # node/edge, annotations, qualified nodes, qualified edges, edges to be added - applicable_rules_threadsafe[piter] = numba.typed.List([(target_node, annotations, qualified_nodes, qualified_edges, edges_to_be_added)]) - - # Merge all threadsafe rules into one single array - for applicable_rule in applicable_rules_threadsafe: - if len(applicable_rule) > 0: - applicable_rules.append(applicable_rule[0]) - - return applicable_rules - - -@numba.njit(cache=True) -def _ground_edge_rule(rule, interpretations_node, interpretations_edge, nodes, edges, neighbors, reverse_neighbors, atom_trace, reverse_graph, edges_to_skip): - # Extract rule params - rule_type = rule.get_type() - clauses = rule.get_clauses() - thresholds = rule.get_thresholds() - ann_fn = rule.get_annotation_function() - rule_edges = rule.get_edges() - - # We return a list of tuples which specify the target nodes/edges that have made the rule body true - applicable_rules = numba.typed.List.empty_list(edge_applicable_rule_type) - - # Create pre-allocated data structure so that parallel code does not need to use "append" to be threadsafe - # One array for each node, then condense into a single list later - applicable_rules_threadsafe = numba.typed.List([numba.typed.List.empty_list(edge_applicable_rule_type) for _ in edges]) - - # Return empty list if rule is not node rule - if rule_type != 'edge': - return applicable_rules - - # Steps - # 1. Loop through all nodes and evaluate each clause with that node and check the truth with the thresholds - # 2. Inside the clause loop it may be necessary to loop through all nodes/edges while grounding the variables - # 3. If the clause is true add the qualified nodes and qualified edges to the atom trace, if on. Break otherwise - # 4. After going through all clauses, add to the annotations list all the annotations of the specified subset. These will be passed to the annotation function - # 5. Finally, if there are any edges to be added, place them in the list - - for piter in prange(len(edges)): - target_edge = edges[piter] - if target_edge in edges_to_skip: - continue - # Initialize dictionary where keys are strings (x1, x2 etc.) and values are lists of qualified neighbors - # Keep track of qualified nodes and qualified edges - # If it's a node clause update (x1 or x2 etc.) qualified neighbors, if it's an edge clause update the qualified neighbors for the source and target (x1, x2) - subsets = numba.typed.Dict.empty(key_type=numba.types.string, value_type=list_of_nodes) - qualified_nodes = numba.typed.List.empty_list(numba.typed.List.empty_list(node_type)) - qualified_edges = numba.typed.List.empty_list(numba.typed.List.empty_list(edge_type)) - annotations = numba.typed.List.empty_list(numba.typed.List.empty_list(interval.interval_type)) - edges_to_be_added = (numba.typed.List.empty_list(node_type), numba.typed.List.empty_list(node_type), rule_edges[-1]) - clause_type_and_variables = numba.typed.List.empty_list(clause_data) - - satisfaction = True - for i, clause in enumerate(clauses): - # Unpack clause variables - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - clause_bnd = clause[3] - clause_operator = clause[4] - - # This is a node clause - # The groundings for node clauses are either the source, target, neighbors of the source node, or an existing subset of nodes - if clause_type == 'node': - clause_var_1 = clause_variables[0] - subset = get_edge_rule_node_clause_subset(clause_var_1, target_edge, subsets, nodes) - - subsets[clause_var_1] = get_qualified_components_node_clause(interpretations_node, subset, clause_label, clause_bnd) - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node', clause_label, numba.typed.List([clause_var_1]))) - - # This is an edge clause - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_source, subset_target = get_edge_rule_edge_clause_subset(clause_var_1, clause_var_2, target_edge, subsets, neighbors, reverse_neighbors, nodes) - - # Get qualified edges - qe = get_qualified_components_edge_clause(interpretations_edge, subset_source, subset_target, clause_label, clause_bnd, reverse_graph) - subsets[clause_var_1] = qe[0] - subsets[clause_var_2] = qe[1] - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - else: - # This is a comparison clause - # Make sure there is at least one ground atom such that pred-num(x) : [1,1] or pred-num(x,y) : [1,1] - # Remember that the predicate in the clause will not contain the "-num" where num is some number. - # We have to remove that manually while checking - # Steps: - # 1. get qualified nodes/edges as well as number associated for first predicate - # 2. get qualified nodes/edges as well as number associated for second predicate - # 3. if there's no number in steps 1 or 2 return false clause - # 4. do comparison with each qualified component from step 1 with each qualified component in step 2 - - # It's a node comparison - if len(clause_variables) == 2: - clause_var_1, clause_var_2 = clause_variables[0], clause_variables[1] - subset_1 = get_edge_rule_node_clause_subset(clause_var_1, target_edge, subsets, nodes) - subset_2 = get_edge_rule_node_clause_subset(clause_var_2, target_edge, subsets, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1, numbers_1 = get_qualified_components_node_comparison_clause(interpretations_node, subset_1, clause_label, clause_bnd) - qualified_nodes_for_comparison_2, numbers_2 = get_qualified_components_node_comparison_clause(interpretations_node, subset_2, clause_label, clause_bnd) - - # It's an edge comparison - elif len(clause_variables) == 4: - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables[0], clause_variables[1], clause_variables[2], clause_variables[3] - subset_1_source, subset_1_target = get_edge_rule_edge_clause_subset(clause_var_1_source, clause_var_1_target, target_edge, subsets, neighbors, reverse_neighbors, nodes) - subset_2_source, subset_2_target = get_edge_rule_edge_clause_subset(clause_var_2_source, clause_var_2_target, target_edge, subsets, neighbors, reverse_neighbors, nodes) - - # 1, 2 - qualified_nodes_for_comparison_1_source, qualified_nodes_for_comparison_1_target, numbers_1 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_1_source, subset_1_target, clause_label, clause_bnd, reverse_graph) - qualified_nodes_for_comparison_2_source, qualified_nodes_for_comparison_2_target, numbers_2 = get_qualified_components_edge_comparison_clause(interpretations_edge, subset_2_source, subset_2_target, clause_label, clause_bnd, reverse_graph) - - # Check if thresholds are satisfied - # If it's a comparison clause we just need to check if the numbers list is not empty (no threshold support) - if clause_type == 'comparison': - if len(numbers_1) == 0 or len(numbers_2) == 0: - satisfaction = False - # Node comparison. Compare stage - elif len(clause_variables) == 2: - satisfaction, qualified_nodes_1, qualified_nodes_2 = compare_numbers_node_predicate(numbers_1, numbers_2, clause_operator, qualified_nodes_for_comparison_1, qualified_nodes_for_comparison_2) - - # Update subsets with final qualified nodes - subsets[clause_var_1] = qualified_nodes_1 - subsets[clause_var_2] = qualified_nodes_2 - - # Save data for annotations and atom trace - clause_type_and_variables.append(('node-comparison', clause_label, numba.typed.List([clause_var_1, clause_var_2]))) - - # Edge comparison. Compare stage - else: - satisfaction, qualified_nodes_1_source, qualified_nodes_1_target, qualified_nodes_2_source, qualified_nodes_2_target = compare_numbers_edge_predicate(numbers_1, numbers_2, clause_operator, - qualified_nodes_for_comparison_1_source, - qualified_nodes_for_comparison_1_target, - qualified_nodes_for_comparison_2_source, - qualified_nodes_for_comparison_2_target) - # Update subsets with final qualified nodes - subsets[clause_var_1_source] = qualified_nodes_1_source - subsets[clause_var_1_target] = qualified_nodes_1_target - subsets[clause_var_2_source] = qualified_nodes_2_source - subsets[clause_var_2_target] = qualified_nodes_2_target - - # Save data for annotations and atom trace - clause_type_and_variables.append(('edge-comparison', clause_label, numba.typed.List([clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target]))) - - # Non comparison clause - else: - if clause_type == 'node': - satisfaction = check_node_clause_satisfaction(interpretations_node, subsets, subset, clause_var_1, clause_label, thresholds[i]) and satisfaction - else: - satisfaction = check_edge_clause_satisfaction(interpretations_edge, subsets, subset_source, subset_target, clause_var_1, clause_label, thresholds[i], reverse_graph) and satisfaction - - # Refine subsets based on any updates - if satisfaction: - satisfaction = refine_subsets_edge_rule(interpretations_edge, clauses, i, subsets, target_edge, neighbors, reverse_neighbors, nodes, thresholds, reverse_graph) and satisfaction - - # Exit loop if even one clause is not satisfied - if not satisfaction: - break - - # Here we are done going through each clause of the rule - # If all clauses we're satisfied, proceed to collect annotations and prepare edges to be added - if satisfaction: - # Loop through the clause data and setup final annotations and trace variables - # 1. Add qualified nodes/edges to trace - # 2. Add annotations to annotation function variable - for i, clause in enumerate(clause_type_and_variables): - clause_type = clause[0] - clause_label = clause[1] - clause_variables = clause[2] - - if clause_type == 'node': - clause_var_1 = clause_variables[0] - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List(subsets[clause_var_1])) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in subsets[clause_var_1]: - a.append(interpretations_node[qn].world[clause_label]) - annotations.append(a) - - elif clause_type == 'edge': - clause_var_1, clause_var_2 = clause_variables - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2]))) - # 2. - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in numba.typed.List(zip(subsets[clause_var_1], subsets[clause_var_2])): - a.append(interpretations_edge[qe].world[clause_label]) - annotations.append(a) - - elif clause_type == 'node-comparison': - clause_var_1, clause_var_2 = clause_variables - qualified_nodes_1 = subsets[clause_var_1] - qualified_nodes_2 = subsets[clause_var_2] - qualified_comparison_nodes = numba.typed.List(qualified_nodes_1) - qualified_comparison_nodes.extend(qualified_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(qualified_comparison_nodes) - qualified_edges.append(numba.typed.List.empty_list(edge_type)) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qn in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - - elif clause_type == 'edge-comparison': - clause_var_1_source, clause_var_1_target, clause_var_2_source, clause_var_2_target = clause_variables - qualified_nodes_1_source = subsets[clause_var_1_source] - qualified_nodes_1_target = subsets[clause_var_1_target] - qualified_nodes_2_source = subsets[clause_var_2_source] - qualified_nodes_2_target = subsets[clause_var_2_target] - qualified_comparison_nodes_1 = numba.typed.List(zip(qualified_nodes_1_source, qualified_nodes_1_target)) - qualified_comparison_nodes_2 = numba.typed.List(zip(qualified_nodes_2_source, qualified_nodes_2_target)) - qualified_comparison_nodes = numba.typed.List(qualified_comparison_nodes_1) - qualified_comparison_nodes.extend(qualified_comparison_nodes_2) - # 1. - if atom_trace: - qualified_nodes.append(numba.typed.List.empty_list(node_type)) - qualified_edges.append(qualified_comparison_nodes) - # 2. - # Add annotations for comparison clause. For now, we don't distinguish between LHS and RHS annotations - if ann_fn != '': - a = numba.typed.List.empty_list(interval.interval_type) - for qe in qualified_comparison_nodes: - a.append(interval.closed(1, 1)) - annotations.append(a) - # node/edge, annotations, qualified nodes, qualified edges, edges to be added - applicable_rules_threadsafe[piter] = numba.typed.List([(target_edge, annotations, qualified_nodes, qualified_edges, edges_to_be_added)]) - - # Merge all threadsafe rules into one single array - for applicable_rule in applicable_rules_threadsafe: - if len(applicable_rule) > 0: - applicable_rules.append(applicable_rule[0]) - - return applicable_rules - - @numba.njit(cache=True) def refine_groundings(clause_variables, groundings, groundings_edges, dependency_graph_neighbors, dependency_graph_reverse_neighbors): # Loop through the dependency graph and refine the groundings that have connections