From 2b88eb7fc22f8c3bdcd7b428fd8bdaa116a6acc0 Mon Sep 17 00:00:00 2001 From: Daphne Odekerken Date: Fri, 5 Jan 2024 22:46:44 +0100 Subject: [PATCH] Chat: added incomplete ASPIC+ visualisation to "backend" tab. --- .../get_incomplete_aspic_graph_data.py | 126 ++++++++++++++++++ src/py_arg_visualisation/pages/50_chat.py | 58 ++++++-- 2 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 src/py_arg_visualisation/functions/graph_data_functions/get_incomplete_aspic_graph_data.py diff --git a/src/py_arg_visualisation/functions/graph_data_functions/get_incomplete_aspic_graph_data.py b/src/py_arg_visualisation/functions/graph_data_functions/get_incomplete_aspic_graph_data.py new file mode 100644 index 0000000..c4b4b7f --- /dev/null +++ b/src/py_arg_visualisation/functions/graph_data_functions/get_incomplete_aspic_graph_data.py @@ -0,0 +1,126 @@ +from typing import Set + +from py_arg.aspic.algorithms.justification.connected_literal import \ + ConnectedLiteral +from py_arg.incomplete_aspic.algorithms.stability.stability_labels import \ + StabilityLabels +from py_arg.incomplete_aspic.classes.incomplete_argumentation_theory import \ + IncompleteArgumentationTheory + + +def get_incomplete_aspic_graph_data( + incomplete_argumentation_theory: IncompleteArgumentationTheory, + stability_labeler_labels: StabilityLabels, + topic_literal: ConnectedLiteral, questions: Set[ConnectedLiteral]): + literal_layers = \ + {literal_str: 1 for literal_str in + incomplete_argumentation_theory.argumentation_system.language} + rule_layers = \ + {rule.id: 1 for rule in + incomplete_argumentation_theory.argumentation_system.rules} + + change = True + todo_rules = topic_literal.children + literal_layers[topic_literal.s1] = 0 + for contrary_literal in topic_literal.contraries_and_contradictories: + literal_layers[contrary_literal.s1] = 0 + todo_rules += contrary_literal.children + + next_layer = -1 + while change: + change = False + + todo_literals = [] + for rule in todo_rules: + if rule_layers[rule.id] > 0: + rule_layers[rule.id] = next_layer + change = True + for antecedent in rule.antecedents: + todo_literals.append(antecedent) + + next_layer -= 1 + + todo_rules = [] + for literal in todo_literals: + if literal_layers[literal.s1] > 0: + literal_layers[literal.s1] = next_layer + for rule in literal.children: + todo_rules.append(rule) + for contra in literal.contraries_and_contradictories: + if literal_layers[contra.s1] > 0: + literal_layers[contra.s1] = next_layer + for rule in contra.children: + todo_rules.append(rule) + next_layer -= 1 + + data_nodes = [] + data_edges = [] + iat = incomplete_argumentation_theory + for literal in iat.argumentation_system.language.values(): + if literal_layers[literal.s1] <= 0: + if literal in questions: + data_nodes.append({'id': literal.s1, 'label': literal.s1, + 'level': literal_layers[literal.s1], + 'shape': 'box', + 'color': 'blue'}) + elif stability_labeler_labels.literal_labeling[ + literal].is_stable_defended: + data_nodes.append({'id': literal.s1, 'label': literal.s1, + 'level': literal_layers[literal.s1], + 'shape': 'box', + 'color': 'green'}) + else: + data_nodes.append({'id': literal.s1, 'label': literal.s1, + 'level': literal_layers[literal.s1], + 'shape': 'box', + 'color': '#c0c0c0'}) + + contra_counter = 0 + for literal in iat.argumentation_system.language.values(): + if literal_layers[literal.s1] <= 0: + for contrary in literal.contraries_and_contradictories: + if literal <= contrary: + contra_id = 'C' + str(contra_counter) + data_edges.append({'id': contra_id, 'arrows': '', + 'from': literal.s1, 'to': contrary.s1, + 'color': {'color': 'red'}, + 'smooth': { + 'enabled': True, + 'type': 'curvedCCW'}}) + contra_counter += 1 + + # Create nodes for defeasible rules + for def_rule in iat.argumentation_system.defeasible_rules: + if rule_layers[def_rule.id] < 0: + data_nodes.append({'id': def_rule.id, 'label': '⇒', + 'shape': 'ellipse', + 'level': rule_layers[def_rule.id]}) + for antecedent in def_rule.antecedents: + data_edges.append( + {'id': antecedent.s1 + '-' + str(def_rule.id), + 'from': antecedent.s1, 'to': def_rule.id, + 'arrows': 'to'}) + data_edges.append( + {'id': str(def_rule.id) + '-' + def_rule.consequent.s1, + 'from': def_rule.id, 'to': def_rule.consequent.s1, + 'arrows': 'to'}) + + # Create nodes for strict rules + for strict_rule in iat.argumentation_system.strict_rules: + if rule_layers[strict_rule.id] < 0: + data_nodes.append({'id': strict_rule.id, 'label': '→', + 'shape': 'ellipse', + 'level': rule_layers[strict_rule.id]}) + for antecedent in strict_rule.antecedents: + data_edges.append( + {'id': antecedent.s1 + '-' + str(strict_rule.id), + 'from': antecedent.s1, 'to': strict_rule.id, + 'arrows': 'to'}) + data_edges.append( + {'id': str(strict_rule.id) + '-' + strict_rule.consequent.s1, + 'from': strict_rule.id, 'to': strict_rule.consequent.s1, + 'arrows': 'to'}) + + data = {'nodes': data_nodes, 'edges': data_edges} + return data + diff --git a/src/py_arg_visualisation/pages/50_chat.py b/src/py_arg_visualisation/pages/50_chat.py index dd086cd..6a09d28 100644 --- a/src/py_arg_visualisation/pages/50_chat.py +++ b/src/py_arg_visualisation/pages/50_chat.py @@ -4,6 +4,7 @@ import random import dash +import visdcc from dash import html, dcc, callback, Input, Output, State import dash_bootstrap_components as dbc @@ -17,6 +18,8 @@ StabilityLabeler from py_arg.incomplete_aspic.classes.incomplete_argumentation_theory import \ IncompleteArgumentationTheory +from py_arg_visualisation.functions.graph_data_functions. \ + get_incomplete_aspic_graph_data import get_incomplete_aspic_graph_data dash.register_page(__name__, name='Inquiry Dialogue System', title='Inquiry Dialogue System') @@ -39,8 +42,32 @@ ] ) right_column = dbc.Col([ - html.B('Topic stability status or next question'), - html.P(id='50-stability-status') + dcc.Tabs([ + dcc.Tab([ + html.B('Topic stability status or next question'), + html.P(id='50-stability-status') + ], label='Frontend'), + dcc.Tab([ + html.B('Visualisation argumentation theory'), + visdcc.Network(data={'nodes': [], 'edges': []}, + id='50-aspic-graph', + options={ + 'physics': {'enabled': True}, + 'solver': 'hierarchicalRepulsion', + 'layout': {'hierarchical': + {'enabled': True, + 'direction': 'DU', + 'sortMethod': 'directed'}}, + 'wind': {'x': 50, 'y': 50}, + 'improvedLayout': True, + 'height': '500px', + 'edges': {'color': { + 'inherit': False, + }}, + 'interaction': {'hover': True}, + }) + ], label='Backend') + ]) ]) layout = html.Div([ html.H1('Argumentation-based inquiry dialogue system'), @@ -138,6 +165,7 @@ def update_knowledge_base_options(queryables, current_value, @callback( Output('50-stability-status', 'children'), + Output('50-aspic-graph', 'data'), State('50-queryables-dropdown', 'value'), Input('50-knowledge-base', 'value'), State('50-argumentation-system', 'data'), @@ -167,15 +195,23 @@ def update_stability_status(positive_queryables, knowledge_base, stability_labeler_labels = StabilityLabeler().label( incomplete_argumentation_theory) + topic_literal = opened_as.language[topic_str] topic_label = stability_labeler_labels.literal_labeling[topic_literal] if topic_label.is_stable: - return 'The topic is ' + topic_label.stability_str.lower() + '.' - - relevance_lister = FourBoolRelevanceLister() - relevance_lister.update(incomplete_argumentation_theory, - stability_labeler_labels) - questions = relevance_lister.relevance_list[topic_literal] - random_question = random.choice(list(questions)) - return 'The topic is not stable yet. Do you know something about ' + \ - random_question.s1 + '?' + user_text = 'The topic is ' + topic_label.stability_str.lower() + '.' + questions = [] + else: + relevance_lister = FourBoolRelevanceLister() + relevance_lister.update(incomplete_argumentation_theory, + stability_labeler_labels) + questions = relevance_lister.relevance_list[topic_literal] + random_question = random.choice(list(questions)) + user_text = 'The topic is not stable yet. Do you know something ' \ + 'about ' + random_question.s1 + '?' + + graph_data = get_incomplete_aspic_graph_data( + incomplete_argumentation_theory, stability_labeler_labels, + topic_literal, questions) + + return user_text, graph_data