""" ARSXAI7.py - Algorithmic Recursive Sequence Analysis with Explainable AI ======================================================================== Universelle Analyseplattform für beliebige Terminalzeichenketten mit automatischer Strukturableitung und XAI-Komponenten. Kernkonzepte: - Laden beliebiger Transkriptdateien mit Kommentaren - Automatische Extraktion der Terminal-Menge - Mehrere Strategien zur Ableitung von Kodierungen und Regeln - Konfidenzmetriken für alle Ableitungen - Interaktive Erklärungskomponente - Visualisierung der Ableitungsprozesse - Progressive Learning aus neuen Daten - Export in verschiedene Formate Version: 7.0 (Vollständige XAI-Integration) """ import sys import subprocess import importlib import warnings import traceback import os import json from datetime import datetime from collections import Counter, defaultdict import threading import queue import re # ============================================================================ # PAKETVERWALTUNG # ============================================================================ warnings.filterwarnings("ignore", category=DeprecationWarning) REQUIRED_PACKAGES = [ 'numpy', 'scipy', 'matplotlib', 'hmmlearn', 'sklearn-crfsuite', 'sentence-transformers', 'networkx', 'torch', 'seaborn', 'tabulate', 'graphviz' # Neu für Automaten-Visualisierung ] def check_and_install_packages(): """Prüft und installiert fehlende Python-Pakete""" print("=" * 70) print("ARSXAI7 - PAKETPRÜFUNG") print("=" * 70) missing_packages = [] for package in REQUIRED_PACKAGES: import_name = package.replace('-', '_') special_imports = { 'sklearn-crfsuite': 'sklearn_crfsuite', 'sentence-transformers': 'sentence_transformers' } import_name = special_imports.get(package, import_name) try: importlib.import_module(import_name) print(f"✓ {package} bereits installiert") except ImportError: print(f"✗ {package} fehlt") missing_packages.append(package) if missing_packages: print("\nInstalliere fehlende Pakete...") for package in missing_packages: try: subprocess.check_call([sys.executable, "-m", "pip", "install", package]) print(f" ✓ {package} erfolgreich installiert") except subprocess.CalledProcessError as e: print(f" ✗ Fehler bei Installation von {package}: {e}") print("\n" + "=" * 70 + "\n") check_and_install_packages() # ============================================================================ # IMPORTS # ============================================================================ import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext import numpy as np from scipy.stats import pearsonr import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure # Optionale Imports mit Status-Erfassung MODULE_STATUS = {} try: import networkx as nx MODULE_STATUS['networkx'] = True except ImportError: MODULE_STATUS['networkx'] = False try: from hmmlearn import hmm MODULE_STATUS['hmmlearn'] = True except ImportError: MODULE_STATUS['hmmlearn'] = False try: from sklearn_crfsuite import CRF MODULE_STATUS['crf'] = True except ImportError: MODULE_STATUS['crf'] = False try: from sentence_transformers import SentenceTransformer MODULE_STATUS['transformer'] = True except ImportError: MODULE_STATUS['transformer'] = False try: import seaborn as sns MODULE_STATUS['seaborn'] = True except ImportError: MODULE_STATUS['seaborn'] = False try: import graphviz MODULE_STATUS['graphviz'] = True except ImportError: MODULE_STATUS['graphviz'] = False # ============================================================================ # THREAD-SICHERE PLOT-FUNKTIONEN # ============================================================================ class PlotThread: """Thread-sichere Plot-Ausführung""" def __init__(self, root): self.root = root self.plot_queue = queue.Queue() self.process() def process(self): try: while True: func, args, kwargs = self.plot_queue.get_nowait() self.root.after(0, lambda: func(*args, **kwargs)) except queue.Empty: pass finally: self.root.after(100, self.process) def plot(self, func, *args, **kwargs): self.plot_queue.put((func, args, kwargs)) # ============================================================================ # DATENVALIDIERUNG # ============================================================================ class DataValidator: """ Prüft die geladenen Transkripte auf Qualität und Konsistenz """ def __init__(self): self.issues = [] self.warnings = [] def validate_chains(self, chains): """Validiert alle Ketten und sammelt Probleme""" self.issues = [] self.warnings = [] if not chains: self.issues.append(("error", "Keine Ketten gefunden")) return self.issues # Prüfe auf leere Ketten empty_chains = [i for i, chain in enumerate(chains) if not chain] if empty_chains: self.warnings.append(("warning", f"Leere Ketten an Positionen: {empty_chains}")) # Prüfe auf Mindestlänge short_chains = [i for i, chain in enumerate(chains) if len(chain) < 2] if short_chains: self.warnings.append(("warning", f"Sehr kurze Ketten (<2 Symbole): {short_chains}")) # Prüfe auf konsistente Symbolschreibweise all_symbols = set() for chain in chains: for symbol in chain: all_symbols.add(symbol) # Prüfe auf mögliche Tippfehler (Ähnlichkeit) symbol_groups = self.group_similar_symbols(all_symbols) for base, similar in symbol_groups.items(): if len(similar) > 1: self.warnings.append(("info", f"Ähnliche Symbole gefunden: {', '.join(similar)}")) return self.issues, self.warnings def group_similar_symbols(self, symbols): """Gruppiert ähnliche Symbole (für Tippfehlererkennung)""" groups = defaultdict(list) for sym in symbols: # Normalisiere: Großbuchstaben, entferne Sonderzeichen normalized = re.sub(r'[^A-Za-z]', '', sym.upper()) groups[normalized].append(sym) return {k: v for k, v in groups.items() if len(v) > 1} def suggest_corrections(self): """Generiert Korrekturvorschläge basierend auf gefundenen Problemen""" suggestions = [] for severity, msg in self.warnings: if "leer" in msg: suggestions.append("Leere Zeilen sollten entfernt werden") elif "kurz" in msg: suggestions.append("Sehr kurze Ketten könnten Rauschen sein") elif "Ähnlich" in msg: suggestions.append("Prüfen Sie auf Tippfehler in den Symbolen") return suggestions # ============================================================================ # ABLEITUNGSSTRATEGIEN FÜR KODIERUNG # ============================================================================ class CodingStrategy: """Basisklasse für Kodierungsstrategien""" def __init__(self, name): self.name = name self.confidence = 0.0 def derive(self, chains): """Leitet Kodierung aus Ketten ab - muss in Subklassen implementiert werden""" raise NotImplementedError def explain(self, symbol, code): """Erklärt, warum ein Symbol so kodiert wurde""" raise NotImplementedError class PositionBasedCoding(CodingStrategy): """ Strategie 1: Kodierung basierend auf Position in der Sequenz - Bit 1: Sprecher (K/V aus Präfix) - Bits 2-3: Phase aus durchschnittlicher Position - Bits 4-5: Subphase aus Wiederholungsmuster """ def __init__(self): super().__init__("Positionsbasierte Kodierung") def derive(self, chains): """Leitet Kodierung aus Positionen ab""" coding = {} # Sammle Positionsstatistiken positions = defaultdict(list) for chain in chains: for i, sym in enumerate(chain): positions[sym].append(i) # Sammle Übergangsmuster transitions = defaultdict(Counter) for chain in chains: for i in range(len(chain)-1): transitions[chain[i]][chain[i+1]] += 1 for symbol in positions: # Bit 1: Sprecher (K=0, V=1) speaker = "0" if symbol.startswith('K') else "1" # Bits 2-3: Phase aus durchschnittlicher Position avg_pos = np.mean(positions[symbol]) max_pos = max([max(pos) for pos in positions.values() if pos]) phase_norm = avg_pos / max_pos if max_pos > 0 else 0 if phase_norm < 0.25: phase = "00" # Begrüßung elif phase_norm < 0.5: phase = "01" # Bedarf elif phase_norm < 0.75: phase = "10" # Abschluss else: phase = "11" # Verabschiedung # Bits 4-5: Subphase aus Wiederholungsmuster # Wie oft wird das Symbol wiederholt? repeat_count = sum(1 for pos_list in positions.values() if len(pos_list) > 1 for pos in pos_list) if repeat_count > len(chains) * 0.3: subphase = "01" # Folge (bei häufigen Wiederholungen) else: subphase = "00" # Basis code = f"{speaker}{phase}{subphase}" coding[symbol] = { 'code': code, 'evidence': { 'avg_position': avg_pos, 'phase_norm': phase_norm, 'repeat_count': repeat_count } } self.confidence = self.calculate_confidence(coding, chains) return coding def calculate_confidence(self, coding, chains): """Berechnet Konfidenz der Ableitung""" # Je konsistenter die Positionen, desto höher die Konfidenz confidence = 0.7 # Basis-Konfidenz return confidence def explain(self, symbol, code_data): """Erklärt die positionsbasierte Kodierung""" code = code_data['code'] evidence = code_data['evidence'] bits = list(code) explanation = [] # Sprecher-Erklärung speaker = "Kunde" if bits[0] == "0" else "Verkäufer" explanation.append(f"Sprecher: {speaker} (aus Präfix '{symbol[0]}')") # Phasen-Erklärung phase_map = {"00": "Begrüßung", "01": "Bedarf", "10": "Abschluss", "11": "Verabschiedung"} phase = phase_map.get(''.join(bits[1:3]), "Unbekannt") explanation.append(f"Phase: {phase} (durchschnittliche Position {evidence['avg_position']:.1f})") # Subphasen-Erklärung subphase = "Folge" if bits[3:] == "01" else "Basis" if bits[3:] == "01": explanation.append(f"Subphase: {subphase} (häufige Wiederholungen)") else: explanation.append(f"Subphase: {subphase} (seltene Wiederholungen)") return " | ".join(explanation) class PatternBasedCoding(CodingStrategy): """ Strategie 2: Kodierung basierend auf wiederkehrenden Mustern - Bit 1: Sprecher (K/V) - Bits 2-3: Phase aus Nachbarschaftsbeziehungen - Bits 4-5: Subphase aus Pattern-Position """ def __init__(self): super().__init__("Musterbasierte Kodierung") def derive(self, chains): """Leitet Kodierung aus Mustern ab""" coding = {} # Finde häufige Patterns patterns = self.find_patterns(chains) # Analysiere Nachbarschaftsbeziehungen neighbors = defaultdict(Counter) for chain in chains: for i, sym in enumerate(chain): if i > 0: neighbors[sym][chain[i-1]] += 1 if i < len(chain)-1: neighbors[sym][chain[i+1]] += 1 for symbol in neighbors: # Bit 1: Sprecher speaker = "0" if symbol.startswith('K') else "1" # Bits 2-3: Phase aus typischen Nachbarn common_neighbors = neighbors[symbol].most_common(3) if any(n[0].endswith('G') for n in common_neighbors): phase = "00" # Begrüßung elif any(n[0].endswith('d') for n in common_neighbors): phase = "01" # Bedarf elif any(n[0].endswith('E') for n in common_neighbors): phase = "10" # Abschluss (Beratung) elif any(n[0].endswith('V') for n in common_neighbors): phase = "11" # Verabschiedung else: phase = "01" # Default: Bedarf # Bits 4-5: Subphase aus Pattern-Teilnahme in_patterns = any(symbol in p for p in patterns) subphase = "01" if in_patterns else "00" code = f"{speaker}{phase}{subphase}" coding[symbol] = { 'code': code, 'evidence': { 'common_neighbors': [(n, c) for n, c in common_neighbors], 'in_patterns': in_patterns, 'patterns_found': len(patterns) } } self.confidence = 0.75 return coding def find_patterns(self, chains, min_length=2, min_occurrences=2): """Findet wiederkehrende Pattern in den Ketten""" patterns = [] pattern_counter = Counter() for chain in chains: for length in range(min_length, min(5, len(chain))): for i in range(len(chain) - length + 1): pattern = tuple(chain[i:i+length]) pattern_counter[pattern] += 1 return [p for p, count in pattern_counter.items() if count >= min_occurrences] def explain(self, symbol, code_data): """Erklärt die musterbasierte Kodierung""" code = code_data['code'] evidence = code_data['evidence'] bits = list(code) explanation = [] explanation.append(f"Sprecher: {'Kunde' if bits[0]=='0' else 'Verkäufer'}") phase_map = {"00": "Begrüßung", "01": "Bedarf", "10": "Abschluss", "11": "Verabschiedung"} phase = phase_map.get(''.join(bits[1:3]), "Unbekannt") neighbor_info = ", ".join([f"{n}({c}x)" for n, c in evidence['common_neighbors'][:2]]) explanation.append(f"Phase: {phase} (häufige Nachbarn: {neighbor_info})") if evidence['in_patterns']: explanation.append(f"Subphase: Folge (Teil von {evidence['patterns_found']} wiederkehrenden Mustern)") else: explanation.append("Subphase: Basis (kein Teil wiederkehrender Muster)") return " | ".join(explanation) class StatisticalBasedCoding(CodingStrategy): """ Strategie 3: Kodierung basierend auf statistischen Verteilungen - Verwendet Korrelationsanalysen und Häufigkeitsverteilungen """ def __init__(self): super().__init__("Statistisch basierte Kodierung") def derive(self, chains): """Leitet Kodierung aus Statistiken ab""" coding = {} # Berechne Häufigkeiten frequencies = defaultdict(int) total_symbols = 0 for chain in chains: for sym in chain: frequencies[sym] += 1 total_symbols += 1 # Berechne Positionen first_positions = {} last_positions = {} for chain in chains: if chain: first_positions[chain[0]] = first_positions.get(chain[0], 0) + 1 last_positions[chain[-1]] = last_positions.get(chain[-1], 0) + 1 # Berechne Übergangsmatrix transitions = defaultdict(Counter) for chain in chains: for i in range(len(chain)-1): transitions[chain[i]][chain[i+1]] += 1 for symbol in frequencies: # Bit 1: Sprecher speaker = "0" if symbol.startswith('K') else "1" # Bits 2-3: Phase basierend auf Position und Häufigkeit rel_freq = frequencies[symbol] / total_symbols first_prob = first_positions.get(symbol, 0) / len(chains) last_prob = last_positions.get(symbol, 0) / len(chains) if first_prob > 0.5: phase = "00" # Meist am Anfang = Begrüßung elif last_prob > 0.5: phase = "11" # Meist am Ende = Verabschiedung elif rel_freq > 0.15: phase = "01" # Häufig in der Mitte = Bedarf else: phase = "10" # Seltener = Abschluss/Beratung # Bits 4-5: Subphase basierend auf Transitionsvielfalt transition_count = len(transitions[symbol]) if transition_count > 3: subphase = "01" # Viele verschiedene Folge-Symbole = Folge else: subphase = "00" # Wenige Folge-Symbole = Basis code = f"{speaker}{phase}{subphase}" coding[symbol] = { 'code': code, 'evidence': { 'frequency': rel_freq, 'first_prob': first_prob, 'last_prob': last_prob, 'transition_count': transition_count } } self.confidence = 0.8 return coding def explain(self, symbol, code_data): """Erklärt die statistisch basierte Kodierung""" code = code_data['code'] evidence = code_data['evidence'] bits = list(code) explanation = [] explanation.append(f"Sprecher: {'Kunde' if bits[0]=='0' else 'Verkäufer'}") phase_map = {"00": "Begrüßung", "01": "Bedarf", "10": "Abschluss", "11": "Verabschiedung"} phase = phase_map.get(''.join(bits[1:3]), "Unbekannt") phase_reason = [] if evidence['first_prob'] > 0.5: phase_reason.append(f"beginnt oft ({evidence['first_prob']:.0%})") if evidence['last_prob'] > 0.5: phase_reason.append(f"endet oft ({evidence['last_prob']:.0%})") if evidence['frequency'] > 0.15: phase_reason.append(f"häufig ({evidence['frequency']:.0%})") explanation.append(f"Phase: {phase} (" + ", ".join(phase_reason) + ")") if evidence['transition_count'] > 3: explanation.append(f"Subphase: Folge ({evidence['transition_count']} verschiedene Folgesymbole)") else: explanation.append(f"Subphase: Basis ({evidence['transition_count']} Folgesymbole)") return " | ".join(explanation) # ============================================================================ # KONFIDENZMETRIKEN # ============================================================================ class ConfidenceMetrics: """ Berechnet Konfidenzwerte für alle abgeleiteten Strukturen """ def __init__(self): self.metrics = {} def calculate_rule_confidence(self, rule, observations): """ Berechnet Konfidenz für eine Regel basierend auf: - Anzahl der Beobachtungen - Konsistenz der Beobachtungen - Stabilität über verschiedene Ketten """ if not observations: return 0.0 n_observations = len(observations) n_unique = len(set(observations)) # Je mehr Beobachtungen, desto höher die Konfidenz (logarithmisch) confidence = min(1.0, np.log10(n_observations + 1) / 2) # Je konsistenter, desto höher die Konfidenz if n_unique == 1: consistency = 1.0 else: # Berechne Entropie der Verteilung counts = Counter(observations) probs = [c/n_observations for c in counts.values()] entropy = -sum(p * np.log2(p) for p in probs) max_entropy = np.log2(len(counts)) consistency = 1 - (entropy / max_entropy) if max_entropy > 0 else 0 confidence = (confidence + consistency) / 2 return round(confidence, 3) def calculate_coding_confidence(self, coding_results): """ Vergleicht Ergebnisse verschiedener Kodierungsstrategien """ if len(coding_results) < 2: return 0.5 # Berechne Übereinstimmung zwischen Strategien agreements = [] symbols = set() for result in coding_results: symbols.update(result.keys()) for symbol in symbols: codes = [r.get(symbol, {}).get('code', None) for r in coding_results] codes = [c for c in codes if c is not None] if len(codes) > 1: # Wie viele stimmen überein? most_common = Counter(codes).most_common(1)[0] agreement = most_common[1] / len(codes) agreements.append(agreement) return np.mean(agreements) if agreements else 0.5 def highlight_uncertain_rules(self, rules, threshold=0.7): """Markiert Regeln mit niedriger Konfidenz""" uncertain = [] for rule, confidence in rules.items(): if confidence < threshold: uncertain.append({ 'rule': rule, 'confidence': confidence, 'suggestion': self.suggest_review(rule, confidence) }) return uncertain def suggest_review(self, rule, confidence): """Generiert Vorschlag zur manuellen Überprüfung""" if confidence < 0.3: return "⚠️ Sehr unsicher - Datenbasis prüfen" elif confidence < 0.5: return "⚠️ Unsicher - Manuelle Validierung empfohlen" elif confidence < 0.7: return "⚠️ Eingeschränkt sicher - Bei Bedarf prüfen" return "✓ Ausreichend sicher" # ============================================================================ # ABLEITUNGSMANAGER # ============================================================================ class DerivationManager: """ Verwaltet verschiedene Ableitungsstrategien und deren Ergebnisse """ def __init__(self): self.strategies = [ PositionBasedCoding(), PatternBasedCoding(), StatisticalBasedCoding() ] self.results = {} self.confidence_metrics = ConfidenceMetrics() self.consensus_coding = {} def derive_all(self, chains): """Wendet alle Strategien an und berechnet Konsens""" self.results = {} for strategy in self.strategies: try: coding = strategy.derive(chains) self.results[strategy.name] = { 'coding': coding, 'confidence': strategy.confidence } except Exception as e: self.results[strategy.name] = { 'error': str(e), 'coding': {} } # Berechne Konsens-Kodierung self.consensus_coding = self.calculate_consensus() return self.results def calculate_consensus(self): """Berechnet Konsens-Kodierung über alle Strategien""" if not self.results: return {} # Sammle alle Symbole all_symbols = set() for result in self.results.values(): if 'coding' in result: all_symbols.update(result['coding'].keys()) consensus = {} for symbol in all_symbols: votes = defaultdict(int) evidences = [] for strategy_name, result in self.results.items(): if 'coding' in result and symbol in result['coding']: code_data = result['coding'][symbol] votes[code_data['code']] += 1 evidences.append({ 'strategy': strategy_name, 'code': code_data['code'], 'evidence': code_data.get('evidence', {}) }) if votes: # Mehrheitsentscheidung most_common = max(votes.items(), key=lambda x: x[1]) consensus[symbol] = { 'code': most_common[0], 'agreement': most_common[1] / len(self.results), 'alternatives': dict(votes), 'evidence': evidences, 'confidence': most_common[1] / len(self.results) } return consensus def get_explanation(self, symbol): """Generiert Erklärung für ein Symbol basierend auf allen Strategien""" if symbol not in self.consensus_coding: return f"Keine Daten für Symbol {symbol}" data = self.consensus_coding[symbol] explanation = [] explanation.append(f"Symbol: {symbol}") explanation.append(f"Konsens-Kodierung: {data['code']}") explanation.append(f"Übereinstimmung: {data['agreement']:.0%}") explanation.append("") explanation.append("Erklärungen der einzelnen Strategien:") for evidence in data['evidence']: explanation.append(f"\n {evidence['strategy']}:") explanation.append(f" Code: {evidence['code']}") if evidence.get('evidence'): for key, value in evidence['evidence'].items(): explanation.append(f" {key}: {value}") return "\n".join(explanation) # ============================================================================ # GENERISCHER ENTSCHEIDUNGSAUTOMAT (KORRIGIERT) # ============================================================================ class GenericDialogueAutomaton: """ Generischer Automat, der Regeln aus Daten lernt """ def __init__(self): self.states = set() self.transitions = {} # (state, symbol) -> new_state self.accepting_states = set() self.current_state = None self.state_counter = 0 self.confidence_metrics = {} # (state, symbol) -> confidence self.state_assignments_cache = {} # Cache für Zustandszuweisungen def learn_from_chains(self, chains): """ Lernt Automaten-Regeln aus beobachteten Ketten """ if not chains: return # Zustände basierend auf Positionen erstellen self.create_states_from_positions(chains) # Übergänge aus beobachteten Transitionen lernen self.learn_transitions(chains) # Akzeptierende Zustände bestimmen self.determine_accepting_states(chains) # Konfidenzen berechnen (JETZT KORREKT IMPLEMENTIERT) self._calculate_confidences(chains) def create_states_from_positions(self, chains): """ Erstellt Zustände basierend auf Positionen in der Sequenz """ # Zustände basierend auf Phasen (0-4) for i in range(5): state_name = f"q_phase_{i}" self.states.add(state_name) self.states.add("q_start") self.states.add("q_error") self.current_state = "q_start" def assign_states(self, chain): """ Weist jeder Position in einer Kette einen Zustand zu """ cache_key = tuple(chain) if cache_key in self.state_assignments_cache: return self.state_assignments_cache[cache_key] states = [] for i, symbol in enumerate(chain): # Einfache positionsbasierte Zustandszuweisung # Phase basierend auf Fortschritt in der Sequenz progress = i / max(1, len(chain) - 1) if progress < 0.2: phase = 0 # Beginn elif progress < 0.4: phase = 1 # Frühe Phase elif progress < 0.6: phase = 2 # Mittlere Phase elif progress < 0.8: phase = 3 # Späte Phase else: phase = 4 # Ende states.append(f"q_phase_{phase}") self.state_assignments_cache[cache_key] = states return states def learn_transitions(self, chains): """ Lernt Übergangswahrscheinlichkeiten aus den Daten """ # Zähle Übergänge transition_counts = defaultdict(Counter) for chain in chains: states = self.assign_states(chain) for i in range(len(chain)-1): curr_state = states[i] next_state = states[i+1] symbol = chain[i+1] # Symbol, das den Übergang auslöst transition_counts[(curr_state, symbol)][next_state] += 1 # Bestimme wahrscheinlichste Übergänge self.transitions = {} for (state, symbol), targets in transition_counts.items(): if targets: most_common = max(targets.items(), key=lambda x: x[1]) self.transitions[(state, symbol)] = most_common[0] def determine_accepting_states(self, chains): """ Bestimmt akzeptierende Zustände (wo Ketten enden) """ end_states = Counter() for chain in chains: if chain: states = self.assign_states(chain) if states: end_states[states[-1]] += 1 # Zustände, in denen mindestens 20% der Ketten enden total = len(chains) for state, count in end_states.items(): if count / total > 0.2: self.accepting_states.add(state) # Falls keine Zustände gefunden, nimm Phase 4 als Standard if not self.accepting_states and "q_phase_4" in self.states: self.accepting_states.add("q_phase_4") def _calculate_confidences(self, chains): """ Berechnet Konfidenzwerte für alle gelernten Übergänge """ # Sammle alle Vorkommen für jeden Übergang transition_occurrences = defaultdict(list) for chain in chains: states = self.assign_states(chain) for i in range(len(chain)-1): curr_state = states[i] symbol = chain[i+1] next_state = states[i+1] key = (curr_state, symbol) transition_occurrences[key].append(next_state == self.transitions.get(key)) # Berechne Konfidenz für jeden Übergang self.confidence_metrics = {} for (state, symbol), occurrences in transition_occurrences.items(): if (state, symbol) in self.transitions: # Konfidenz = Anteil der korrekten Vorhersagen correct = sum(occurrences) total = len(occurrences) confidence = correct / total if total > 0 else 0 # Zusätzlich: Berücksichtige Anzahl der Beobachtungen # Je mehr Beobachtungen, desto höher die Konfidenz observation_factor = min(1.0, total / 10) # Bei 10+ Beobachtungen maximal confidence = confidence * 0.7 + observation_factor * 0.3 self.confidence_metrics[(state, symbol)] = round(confidence, 3) def transition(self, symbol): """ Führt einen Übergang basierend auf dem Symbol durch """ if self.current_state is None: self.current_state = "q_start" key = (self.current_state, symbol) if key in self.transitions: self.current_state = self.transitions[key] return self.current_state, True, self.confidence_metrics.get(key, 0.5) else: self.current_state = "q_error" return "q_error", False, 0.0 def validate_chain(self, chain): """ Validiert eine ganze Kette """ self.current_state = "q_start" protocol = [] first_error = None for i, symbol in enumerate(chain): new_state, success, confidence = self.transition(symbol) protocol.append({ 'position': i + 1, 'symbol': symbol, 'state': new_state, 'success': success, 'confidence': confidence }) if new_state == "q_error" and first_error is None: # Finde mögliche gültige Übergänge für Erklärung possible_transitions = [] for (state, sym), next_state in self.transitions.items(): if state == self.current_state: possible_transitions.append(f"{sym}→{next_state}") explanation = f"Kein gültiger Übergang von {self.current_state}" if possible_transitions: explanation += f"\nMögliche Übergänge: {', '.join(possible_transitions[:3])}" first_error = { 'position': i + 1, 'symbol': symbol, 'explanation': explanation } valid = self.current_state in self.accepting_states and first_error is None return valid, self.current_state, protocol, first_error def get_rules_string(self): """ Gibt die gelernten Regeln als String zurück """ lines = [] lines.append("GELERNTE AUTOMATEN-REGELN:") lines.append("=" * 60) if not self.transitions: lines.append(" Keine Regeln gelernt.") return "\n".join(lines) # Gruppiere nach Ausgangszustand rules_by_state = defaultdict(list) for (state, symbol), next_state in self.transitions.items(): rules_by_state[state].append((symbol, next_state)) for state in sorted(rules_by_state.keys()): lines.append(f"\n{state}:") for symbol, next_state in sorted(rules_by_state[state]): conf = self.confidence_metrics.get((state, symbol), 0) conf_stars = "★" * int(conf * 5) + "☆" * (5 - int(conf * 5)) lines.append(f" {symbol} → {next_state} {conf_stars} ({conf:.0%})") lines.append(f"\nAkzeptierende Zustände: {', '.join(sorted(self.accepting_states))}") return "\n".join(lines) # ============================================================================ # INTERAKTIVE ERKLÄRUNGSKOMPONENTE # ============================================================================ class InteractiveExplainer: """ Ermöglicht dem Nutzer, nach Gründen für Ableitungen zu fragen """ def __init__(self, derivation_manager, automaton): self.derivation_manager = derivation_manager self.automaton = automaton self.question_history = [] def why_this_coding(self, symbol): """Warum wurde Symbol genau so kodiert?""" if symbol not in self.derivation_manager.consensus_coding: return f"Symbol '{symbol}' nicht gefunden" data = self.derivation_manager.consensus_coding[symbol] explanation = [] explanation.append(f"🔍 Erklärung für Kodierung von '{symbol}':") explanation.append("=" * 60) explanation.append(f"Konsens-Kodierung: {data['code']}") explanation.append(f"Übereinstimmung: {data['agreement']:.0%}") explanation.append("") # Zeige, wie jede Strategie entschieden hat explanation.append("Einzelne Strategien:") for evidence in data['evidence']: explanation.append(f"\n 📊 {evidence['strategy']}:") explanation.append(f" Code: {evidence['code']}") # Detailerklärung der Strategie strategy = next((s for s in self.derivation_manager.strategies if s.name == evidence['strategy']), None) if strategy: detailed = strategy.explain(symbol, evidence) explanation.append(f" → {detailed}") self.question_history.append(('coding', symbol)) return "\n".join(explanation) def why_this_transition(self, from_state, symbol, to_state): """Warum wurde dieser Übergang gelernt?""" explanation = [] explanation.append(f"🔍 Erklärung für Übergang:") explanation.append(f" {from_state} --({symbol})--> {to_state}") explanation.append("=" * 60) # Suche nach Belegen in den Daten confidence = self.automaton.confidence_metrics.get((from_state, symbol), 0) explanation.append(f"Konfidenz: {confidence:.0%}") if confidence > 0.7: explanation.append("✓ Dieser Übergang wurde häufig und konsistent beobachtet") elif confidence > 0.4: explanation.append("⚠️ Dieser Übergang wurde mehrfach, aber nicht immer konsistent beobachtet") else: explanation.append("⚠️ Dieser Übergang wurde selten beobachtet") self.question_history.append(('transition', f"{from_state}--{symbol}")) return "\n".join(explanation) def what_if(self, symbol, alternative_code): """ Was wäre, wenn man anders kodiert hätte? Simuliert die Auswirkungen einer alternativen Kodierung """ explanation = [] explanation.append(f"🔮 Simulation: '{symbol}' als {alternative_code} kodieren") explanation.append("=" * 60) # Vergleiche mit aktueller Kodierung current = self.derivation_manager.consensus_coding.get(symbol, {}) if current: explanation.append(f"Aktuelle Kodierung: {current.get('code', '?')}") explanation.append(f"Abweichung: {self.hamming_distance(current.get('code', ''), alternative_code)} Bit(s)") # Zeige mögliche Konsequenzen explanation.append("\nMögliche Auswirkungen:") explanation.append(" • Automaten-Regeln müssten neu gelernt werden") explanation.append(" • Phasenzuordnung würde sich ändern") explanation.append(" • Statistiken würden sich verschieben") return "\n".join(explanation) def hamming_distance(self, code1, code2): """Berechnet Hamming-Distanz zwischen zwei Codes""" if len(code1) != len(code2): return -1 return sum(c1 != c2 for c1, c2 in zip(code1, code2)) def get_history_string(self): """Gibt den Frageverlauf aus""" lines = ["📋 Frageverlauf:"] for i, (qtype, subject) in enumerate(self.question_history[-10:], 1): if qtype == 'coding': lines.append(f" {i}. Kodierung von '{subject}'") else: lines.append(f" {i}. Übergang {subject}") return "\n".join(lines) # ============================================================================ # PROGRESSIVE LEARNING # ============================================================================ class ProgressiveLearner: """ Lernt kontinuierlich aus neuen Daten und passt Regeln an """ def __init__(self): self.knowledge_base = { 'versions': [], 'current_version': 0 } self.learning_rate = 0.1 def incorporate_new_data(self, chains, current_coding, current_rules): """ Integriert neue Daten und aktualisiert Regeln """ version = { 'timestamp': datetime.now().isoformat(), 'n_chains': len(chains), 'coding': current_coding.copy() if current_coding else {}, 'rules': current_rules.copy() if current_rules else {}, 'changes': [] } # Vergleiche mit vorheriger Version if self.knowledge_base['versions']: prev = self.knowledge_base['versions'][-1] changes = self.detect_changes(prev, version) version['changes'] = changes self.knowledge_base['versions'].append(version) self.knowledge_base['current_version'] = len(self.knowledge_base['versions']) - 1 return version def detect_changes(self, prev, current): """Erkennt Änderungen zwischen zwei Versionen""" changes = [] # Kodierungs-Änderungen prev_coding = prev.get('coding', {}) curr_coding = current.get('coding', {}) for symbol, code_data in curr_coding.items(): prev_code = prev_coding.get(symbol, {}).get('code') curr_code = code_data.get('code') if prev_code and curr_code and prev_code != curr_code: changes.append({ 'type': 'coding_change', 'symbol': symbol, 'old': prev_code, 'new': curr_code, 'reason': 'Angepasst an neue Daten' }) return changes def show_evolution(self): """Zeigt, wie sich Regeln über Zeit verändert haben""" if not self.knowledge_base['versions']: return "Keine Versionshistorie verfügbar" lines = [] lines.append("📈 EVOLUTION DER ABGELEITETEN STRUKTUREN") lines.append("=" * 60) for i, version in enumerate(self.knowledge_base['versions']): lines.append(f"\nVersion {i+1} - {version['timestamp']}") lines.append(f" {version['n_chains']} Ketten analysiert") if version['changes']: lines.append(" Änderungen:") for change in version['changes']: lines.append(f" • {change['symbol']}: {change['old']} → {change['new']}") return "\n".join(lines) # ============================================================================ # VISUALISIERUNGSKOMPONENTEN # ============================================================================ class DerivationVisualizer: """ Visualisiert den Ableitungsprozess """ def __init__(self, root, plot_thread): self.root = root self.plot_thread = plot_thread def plot_coding_comparison(self, coding_results, symbols): """Vergleicht Kodierungsergebnisse verschiedener Strategien""" if not coding_results or not symbols: return fig, axes = plt.subplots(len(coding_results), 1, figsize=(12, 4*len(coding_results))) if len(coding_results) == 1: axes = [axes] for idx, (strategy_name, result) in enumerate(coding_results.items()): ax = axes[idx] # Extrahiere Codes für die ersten 10 Symbole display_symbols = symbols[:10] codes = [] for sym in display_symbols: if sym in result.get('coding', {}): code = result['coding'][sym].get('code', '?????') codes.append(code) else: codes.append('?????') # Erstelle Heatmap-ähnliche Darstellung code_matrix = [[int(bit) for bit in code] for code in codes] im = ax.imshow(code_matrix, cmap='Blues', aspect='auto', interpolation='nearest') ax.set_xticks(range(5)) ax.set_xticklabels(['Bit1\nSprecher', 'Bit2\nPhase', 'Bit3\nPhase', 'Bit4\nSub', 'Bit5\nSub']) ax.set_yticks(range(len(display_symbols))) ax.set_yticklabels(display_symbols) ax.set_title(f"{strategy_name} (Konfidenz: {result.get('confidence', 0):.0%})") # Werte in Zellen schreiben for i in range(len(display_symbols)): for j in range(5): text = ax.text(j, i, code_matrix[i][j], ha='center', va='center') plt.tight_layout() self.plot_thread.plot(lambda: plt.show()) def plot_confidence_heatmap(self, automaton): """Zeigt Konfidenzen der Regeln als Heatmap""" if not automaton or not automaton.transitions: return # Sammle alle Zustände und Symbole states = sorted(set(s for (s, _) in automaton.transitions.keys())) symbols = sorted(set(sym for (_, sym) in automaton.transitions.keys())) # Erstelle Konfidenz-Matrix confidence_matrix = np.zeros((len(states), len(symbols))) for i, state in enumerate(states): for j, symbol in enumerate(symbols): confidence_matrix[i, j] = automaton.confidence_metrics.get((state, symbol), 0) fig, ax = plt.subplots(figsize=(10, 6)) im = ax.imshow(confidence_matrix, cmap='YlOrRd', aspect='auto', vmin=0, vmax=1) ax.set_xticks(range(len(symbols))) ax.set_xticklabels(symbols, rotation=45) ax.set_yticks(range(len(states))) ax.set_yticklabels(states) ax.set_title("Konfidenz der Automaten-Regeln") plt.colorbar(im, ax=ax, label='Konfidenz') plt.tight_layout() self.plot_thread.plot(lambda: plt.show()) def plot_automaton_graph(self, automaton): """Visualisiert den Automaten als Graph""" if not MODULE_STATUS['graphviz']: return dot = graphviz.Digraph(comment='Gelernter Automat') # Zustände hinzufügen for state in automaton.states: if state in automaton.accepting_states: dot.node(state, state, shape='doublecircle') elif state == 'q_error': dot.node(state, state, shape='box', color='red') else: dot.node(state, state, shape='circle') # Übergänge hinzufügen for (state, symbol), next_state in automaton.transitions.items(): conf = automaton.confidence_metrics.get((state, symbol), 0) label = f"{symbol}\n({conf:.0%})" dot.edge(state, next_state, label=label) self.plot_thread.plot(lambda: dot.render('automaton_graph', view=True)) # ============================================================================ # MULTI-FORMAT EXPORTER # ============================================================================ class MultiFormatExporter: """ Exportiert Ergebnisse in verschiedene Formate """ def __init__(self): self.export_path = "exports" os.makedirs(self.export_path, exist_ok=True) def to_json(self, data, filename=None): """Exportiert als JSON""" if filename is None: filename = f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" filepath = os.path.join(self.export_path, filename) with open(filepath, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False, default=str) return filepath def to_csv(self, data, filename=None): """Exportiert als CSV""" if filename is None: filename = f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv" filepath = os.path.join(self.export_path, filename) import csv with open(filepath, 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) if 'coding' in data: writer.writerow(['Symbol', 'Code', 'Konfidenz']) for symbol, info in data['coding'].items(): writer.writerow([symbol, info.get('code', ''), info.get('confidence', '')]) return filepath def to_html(self, data, filename=None): """Exportiert als interaktiven HTML-Bericht""" if filename is None: filename = f"report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html" filepath = os.path.join(self.export_path, filename) html = [] html.append("") html.append("") html.append("ARSXAI7 Analysebericht") html.append("") html.append("") html.append("") html.append(f"

ARSXAI7 Analysebericht

") html.append(f"

Generiert: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

") if 'coding' in data: html.append("

Kodierungsergebnisse

") html.append("") html.append("") for symbol, info in data['coding'].items(): conf = info.get('confidence', 0) conf_class = "confidence-high" if conf > 0.7 else "confidence-medium" if conf > 0.4 else "confidence-low" html.append(f"") html.append(f"") html.append(f"") html.append(f"") html.append(f"") html.append("
SymbolCodeKonfidenz
{symbol}{info.get('code', '')}{conf:.0%}
") html.append("") html.append("") with open(filepath, 'w', encoding='utf-8') as f: f.write("\n".join(html)) return filepath def to_latex(self, data, filename=None): """Exportiert als LaTeX für wissenschaftliche Publikationen""" if filename is None: filename = f"export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.tex" filepath = os.path.join(self.export_path, filename) latex = [] latex.append("\\documentclass{article}") latex.append("\\usepackage[utf8]{inputenc}") latex.append("\\usepackage{booktabs}") latex.append("\\begin{document}") latex.append("\\section{ARSXAI7 Analyseergebnisse}") if 'coding' in data: latex.append("\\subsection{Kodierung der Terminalzeichen}") latex.append("\\begin{tabular}{lll}") latex.append("\\toprule") latex.append("Symbol & Code & Konfidenz \\\\") latex.append("\\midrule") for symbol, info in data['coding'].items(): latex.append(f"{symbol} & {info.get('code', '')} & {info.get('confidence', 0):.0%} \\\\") latex.append("\\bottomrule") latex.append("\\end{tabular}") latex.append("\\end{document}") with open(filepath, 'w', encoding='utf-8') as f: f.write("\n".join(latex)) return filepath # ============================================================================ # GUI - HAUPTFENSTER # ============================================================================ class ARSXAI7GUI: """Haupt-GUI für ARSXAI7""" def __init__(self, root): self.root = root self.root.title("ARSXAI7 - Algorithmic Recursive Sequence Analysis with Explainable AI") self.root.geometry("1600x1000") # Threading und Updates self.plot_thread = PlotThread(root) self.update_queue = queue.Queue() self.process_updates() # Datenstrukturen self.chains = [] self.terminals = set() self.comments = [] self.delimiter = tk.StringVar(value=",") # Analysekomponenten self.validator = DataValidator() self.derivation_manager = DerivationManager() self.automaton = GenericDialogueAutomaton() self.explainer = None self.learner = ProgressiveLearner() self.visualizer = DerivationVisualizer(root, self.plot_thread) self.exporter = MultiFormatExporter() # GUI-Elemente self.create_menu() self.create_main_panels() self.status_var = tk.StringVar(value="Bereit") self.create_statusbar() # Modulstatus anzeigen self.show_module_status() def process_updates(self): """Verarbeitet asynchrone GUI-Updates""" try: while True: update_func = self.update_queue.get_nowait() update_func() except queue.Empty: pass finally: self.root.after(100, self.process_updates) def safe_gui_update(self, func): """Führt Funktion im Hauptthread aus""" self.update_queue.put(func) def create_menu(self): """Erstellt die Menüleiste""" menubar = tk.Menu(self.root) self.root.config(menu=menubar) # Datei-Menü file_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Datei", menu=file_menu) file_menu.add_command(label="Transkripte laden", command=self.load_transcripts) file_menu.add_command(label="Beispiel laden", command=self.load_example) file_menu.add_separator() file_menu.add_command(label="Exportieren", command=self.show_export_dialog) file_menu.add_separator() file_menu.add_command(label="Beenden", command=self.root.quit) # Analyse-Menü analyze_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Analyse", menu=analyze_menu) analyze_menu.add_command(label="Alle Strategien anwenden", command=self.run_all_strategies) analyze_menu.add_command(label="Automaten lernen", command=self.learn_automaton) analyze_menu.add_command(label="Validierung durchführen", command=self.run_validation) # XAI-Menü xai_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="XAI", menu=xai_menu) xai_menu.add_command(label="Erklärung für Symbol", command=self.ask_explanation) xai_menu.add_command(label="Regeln erklären", command=self.explain_rules) xai_menu.add_command(label="Was-wäre-wenn Simulation", command=self.what_if_dialog) # Visualisierung-Menü vis_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Visualisierung", menu=vis_menu) vis_menu.add_command(label="Kodierungsvergleich", command=self.plot_coding_comparison) vis_menu.add_command(label="Konfidenz-Heatmap", command=self.plot_confidence) vis_menu.add_command(label="Automaten-Graph", command=self.plot_automaton) # Hilfe-Menü help_menu = tk.Menu(menubar, tearoff=0) menubar.add_cascade(label="Hilfe", menu=help_menu) help_menu.add_command(label="Modulstatus", command=self.show_module_status) help_menu.add_command(label="Evolution anzeigen", command=self.show_evolution) help_menu.add_command(label="Über", command=self.show_about) def create_main_panels(self): """Erstellt die Haupt-Panels""" # Haupt-PanedWindow main_paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL) main_paned.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # Linkes Panel - Eingabe left_frame = ttk.Frame(main_paned) main_paned.add(left_frame, weight=1) self.create_input_panel(left_frame) # Rechtes Panel - Ausgabe mit Notebook right_frame = ttk.Frame(main_paned) main_paned.add(right_frame, weight=3) self.create_output_panel(right_frame) def create_input_panel(self, parent): """Erstellt das Eingabe-Panel""" # Titel ttk.Label(parent, text="Eingabe", font=('Arial', 12, 'bold')).pack(anchor=tk.W, pady=5) # Trennzeichen-Auswahl delim_frame = ttk.Frame(parent) delim_frame.pack(fill=tk.X, pady=5) ttk.Label(delim_frame, text="Trennzeichen:").pack(side=tk.LEFT) ttk.Radiobutton(delim_frame, text="Komma (,)", variable=self.delimiter, value=",").pack(side=tk.LEFT, padx=2) ttk.Radiobutton(delim_frame, text="Semikolon (;)", variable=self.delimiter, value=";").pack(side=tk.LEFT, padx=2) ttk.Radiobutton(delim_frame, text="Leerzeichen", variable=self.delimiter, value=" ").pack(side=tk.LEFT, padx=2) self.custom_delimiter = ttk.Entry(delim_frame, width=5) self.custom_delimiter.pack(side=tk.LEFT, padx=2) self.custom_delimiter.insert(0, "|") # Text-Eingabe ttk.Label(parent, text="Transkripte (eine pro Zeile, # für Kommentare):").pack(anchor=tk.W, pady=5) self.text_input = scrolledtext.ScrolledText(parent, height=15, font=('Courier', 10)) self.text_input.pack(fill=tk.BOTH, expand=True, pady=5) # Buttons btn_frame = ttk.Frame(parent) btn_frame.pack(fill=tk.X, pady=5) ttk.Button(btn_frame, text="Datei laden", command=self.load_file).pack(side=tk.LEFT, padx=2) ttk.Button(btn_frame, text="Parsen", command=self.parse_input).pack(side=tk.LEFT, padx=2) ttk.Button(btn_frame, text="Beispiel", command=self.load_example).pack(side=tk.LEFT, padx=2) # Info-Label self.info_var = tk.StringVar(value="Keine Daten geladen") ttk.Label(parent, textvariable=self.info_var, foreground="blue").pack(anchor=tk.W, pady=5) # Validierungswarnungen self.warning_text = scrolledtext.ScrolledText(parent, height=5, font=('Courier', 9), foreground="orange") self.warning_text.pack(fill=tk.X, pady=5) def create_output_panel(self, parent): """Erstellt das Ausgabe-Panel mit Notebook-Tabs""" self.notebook = ttk.Notebook(parent) self.notebook.pack(fill=tk.BOTH, expand=True) # Tab 1: Kodierungsergebnisse self.tab_coding = ttk.Frame(self.notebook) self.notebook.add(self.tab_coding, text="Kodierung") self.create_coding_tab() # Tab 2: Automat self.tab_automaton = ttk.Frame(self.notebook) self.notebook.add(self.tab_automaton, text="Automat") self.create_automaton_tab() # Tab 3: Erklärungen (XAI) self.tab_xai = ttk.Frame(self.notebook) self.notebook.add(self.tab_xai, text="XAI - Erklärungen") self.create_xai_tab() # Tab 4: Statistiken self.tab_stats = ttk.Frame(self.notebook) self.notebook.add(self.tab_stats, text="Statistiken") self.create_statistics_tab() # Tab 5: Evolution self.tab_evolution = ttk.Frame(self.notebook) self.notebook.add(self.tab_evolution, text="Evolution") self.create_evolution_tab() def create_coding_tab(self): """Erstellt den Kodierungs-Tab""" # Steuerung control = ttk.Frame(self.tab_coding) control.pack(fill=tk.X, pady=5) ttk.Button(control, text="Alle Strategien", command=self.run_all_strategies).pack(side=tk.LEFT, padx=5) ttk.Button(control, text="Konsens berechnen", command=self.show_consensus).pack(side=tk.LEFT, padx=5) # Text-Ausgabe self.text_coding = scrolledtext.ScrolledText(self.tab_coding, font=('Courier', 10)) self.text_coding.pack(fill=tk.BOTH, expand=True, pady=5) def create_automaton_tab(self): """Erstellt den Automaten-Tab""" control = ttk.Frame(self.tab_automaton) control.pack(fill=tk.X, pady=5) ttk.Button(control, text="Automaten lernen", command=self.learn_automaton).pack(side=tk.LEFT, padx=5) ttk.Button(control, text="Kette validieren", command=self.validate_chain).pack(side=tk.LEFT, padx=5) self.text_automaton = scrolledtext.ScrolledText(self.tab_automaton, font=('Courier', 10)) self.text_automaton.pack(fill=tk.BOTH, expand=True, pady=5) def create_xai_tab(self): """Erstellt den XAI-Tab""" # Eingabe für Fragen question_frame = ttk.Frame(self.tab_xai) question_frame.pack(fill=tk.X, pady=5) ttk.Label(question_frame, text="Symbol:").pack(side=tk.LEFT) self.symbol_entry = ttk.Entry(question_frame, width=10) self.symbol_entry.pack(side=tk.LEFT, padx=5) ttk.Button(question_frame, text="Warum diese Kodierung?", command=self.ask_explanation).pack(side=tk.LEFT, padx=5) ttk.Button(question_frame, text="Was-wäre-wenn", command=self.what_if_dialog).pack(side=tk.LEFT, padx=5) # Text-Ausgabe für Erklärungen self.text_xai = scrolledtext.ScrolledText(self.tab_xai, font=('Courier', 10)) self.text_xai.pack(fill=tk.BOTH, expand=True, pady=5) def create_statistics_tab(self): """Erstellt den Statistik-Tab""" control = ttk.Frame(self.tab_stats) control.pack(fill=tk.X, pady=5) ttk.Button(control, text="Statistiken berechnen", command=self.calculate_statistics).pack(side=tk.LEFT, padx=5) self.text_stats = scrolledtext.ScrolledText(self.tab_stats, font=('Courier', 10)) self.text_stats.pack(fill=tk.BOTH, expand=True, pady=5) def create_evolution_tab(self): """Erstellt den Evolution-Tab""" control = ttk.Frame(self.tab_evolution) control.pack(fill=tk.X, pady=5) ttk.Button(control, text="Evolution anzeigen", command=self.show_evolution).pack(side=tk.LEFT, padx=5) self.text_evolution = scrolledtext.ScrolledText(self.tab_evolution, font=('Courier', 10)) self.text_evolution.pack(fill=tk.BOTH, expand=True, pady=5) def create_statusbar(self): """Erstellt die Statusleiste""" status = ttk.Frame(self.root) status.pack(side=tk.BOTTOM, fill=tk.X) ttk.Label(status, textvariable=self.status_var).pack(side=tk.LEFT, padx=5) self.progress_bar = ttk.Progressbar(status, length=100, mode='indeterminate') self.progress_bar.pack(side=tk.RIGHT, padx=5) def get_actual_delimiter(self): """Gibt das tatsächliche Trennzeichen zurück""" delim = self.delimiter.get() if delim == "custom": return self.custom_delimiter.get() return delim def parse_line(self, line): """Parst eine einzelne Zeile in Symbole""" line = line.strip() if not line or line.startswith('#'): return [] delim = self.get_actual_delimiter() if delim == " ": parts = re.split(r'\s+', line) else: parts = line.split(delim) return [p.strip() for p in parts if p.strip()] def parse_input(self): """Parst die Eingabe und extrahiert Ketten und Kommentare""" text = self.text_input.get("1.0", tk.END) lines = text.strip().split('\n') self.chains = [] self.comments = [] for line in lines: line = line.strip() if not line: continue if line.startswith('#'): self.comments.append(line) continue chain = self.parse_line(line) if chain: self.chains.append(chain) if self.chains: # Extrahiere Terminal-Menge self.terminals = set() for chain in self.chains: for symbol in chain: self.terminals.add(symbol) self.info_var.set(f"{len(self.chains)} Ketten, {len(self.terminals)} Terminale") self.status_var.set(f"{len(self.chains)} Ketten geladen") # Validierung durchführen self.run_validation() # Automatische Analyse starten self.run_all_strategies() else: messagebox.showwarning("Warnung", "Keine gültigen Ketten gefunden!") def run_validation(self): """Führt die Datenvalidierung durch""" if not self.chains: return issues, warnings = self.validator.validate_chains(self.chains) self.warning_text.delete("1.0", tk.END) if warnings: self.warning_text.insert(tk.END, "VALIDIERUNGSWARNUNGEN:\n") for severity, msg in warnings: self.warning_text.insert(tk.END, f" {msg}\n") suggestions = self.validator.suggest_corrections() if suggestions: self.warning_text.insert(tk.END, "\nKORREKTURVORSCHLÄGE:\n") for s in suggestions: self.warning_text.insert(tk.END, f" • {s}\n") else: self.warning_text.insert(tk.END, "✓ Keine Validierungsprobleme gefunden") def run_all_strategies(self): """Wendet alle Kodierungsstrategien an""" if not self.chains: messagebox.showerror("Fehler", "Keine Daten geladen!") return self.status_var.set("Wende Kodierungsstrategien an...") self.progress_bar.start() def run(): try: results = self.derivation_manager.derive_all(self.chains) def update(): self.text_coding.delete("1.0", tk.END) self.text_coding.insert(tk.END, "ERGEBNISSE DER KODIERUNGSSTRATEGIEN\n") self.text_coding.insert(tk.END, "=" * 70 + "\n\n") for strategy_name, result in results.items(): self.text_coding.insert(tk.END, f"\n{strategy_name}:\n") self.text_coding.insert(tk.END, "-" * 40 + "\n") if 'error' in result: self.text_coding.insert(tk.END, f"Fehler: {result['error']}\n") else: conf = result.get('confidence', 0) self.text_coding.insert(tk.END, f"Konfidenz: {conf:.0%}\n\n") for symbol, code_data in list(result['coding'].items())[:15]: code = code_data.get('code', '?????') self.text_coding.insert(tk.END, f" {symbol}: {code}\n") # Konsens anzeigen self.show_consensus() # Erklärer initialisieren self.explainer = InteractiveExplainer(self.derivation_manager, self.automaton) self.status_var.set("Kodierung abgeschlossen") self.progress_bar.stop() self.safe_gui_update(update) except Exception as e: def error(): messagebox.showerror("Fehler", f"Analyse fehlgeschlagen:\n{str(e)}") self.progress_bar.stop() self.safe_gui_update(error) thread = threading.Thread(target=run) thread.daemon = True thread.start() def show_consensus(self): """Zeigt den Konsens aller Strategien""" if not self.derivation_manager.consensus_coding: return self.text_coding.insert(tk.END, "\n" + "=" * 70 + "\n") self.text_coding.insert(tk.END, "KONSENS-KODIERUNG (Mehrheitsentscheidung)\n") self.text_coding.insert(tk.END, "=" * 70 + "\n\n") for symbol, data in sorted(self.derivation_manager.consensus_coding.items()): agreement = data.get('agreement', 0) conf_color = "✓" if agreement > 0.66 else "⚠️" if agreement > 0.33 else "❌" self.text_coding.insert(tk.END, f"{conf_color} {symbol}: {data['code']} (Übereinstimmung: {agreement:.0%})\n") def learn_automaton(self): """Lernt Automaten-Regeln aus den Daten""" if not self.chains: messagebox.showerror("Fehler", "Keine Daten geladen!") return self.status_var.set("Lerne Automaten-Regeln...") try: self.automaton.learn_from_chains(self.chains) self.text_automaton.delete("1.0", tk.END) self.text_automaton.insert(tk.END, self.automaton.get_rules_string()) # Progressive Learning version = self.learner.incorporate_new_data( self.chains, self.derivation_manager.consensus_coding, self.automaton.transitions ) self.status_var.set(f"Automaten gelernt (Version {self.learner.knowledge_base['current_version']+1})") except Exception as e: messagebox.showerror("Fehler", f"Automaten-Lernen fehlgeschlagen:\n{str(e)}") def validate_chain(self): """Validiert eine ausgewählte Kette""" if not self.chains or not self.automaton.transitions: messagebox.showerror("Fehler", "Keine Daten oder kein Automat!") return # Einfachen Dialog für Kettenauswahl dialog = tk.Toplevel(self.root) dialog.title("Kette auswählen") dialog.geometry("400x300") ttk.Label(dialog, text="Verfügbare Ketten:").pack(pady=5) listbox = tk.Listbox(dialog) listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) for i, chain in enumerate(self.chains[:10]): listbox.insert(tk.END, f"{i+1}: {' → '.join(chain[:10])}...") def validate_selected(): selection = listbox.curselection() if selection: idx = selection[0] chain = self.chains[idx] valid, state, protocol, error = self.automaton.validate_chain(chain) result_text = [] result_text.append(f"VALIDIERUNG KETTE {idx+1}\n") result_text.append("=" * 50 + "\n") result_text.append(f"Ergebnis: {'✓ GÜLTIG' if valid else '✗ UNGÜLTIG'}\n") result_text.append(f"Endzustand: {state}\n\n") if error: result_text.append(f"❌ Fehler bei Position {error['position']}: {error['symbol']}\n") result_text.append(f" {error['explanation']}\n\n") result_text.append("Entscheidungspfad:\n") for step in protocol: result_text.append( f" {step['position']}: {step['symbol']} → {step['state']} " f"(Konf: {step['confidence']:.0%})\n" ) self.text_automaton.insert(tk.END, "\n" + "\n".join(result_text)) dialog.destroy() ttk.Button(dialog, text="Validieren", command=validate_selected).pack(pady=5) def ask_explanation(self): """Fragt nach Erklärung für ein Symbol""" if not self.explainer: messagebox.showerror("Fehler", "Keine Analyse vorhanden!") return symbol = self.symbol_entry.get().strip() if not symbol: messagebox.showwarning("Warnung", "Bitte ein Symbol eingeben!") return explanation = self.explainer.why_this_coding(symbol) self.text_xai.delete("1.0", tk.END) self.text_xai.insert(tk.END, explanation) self.text_xai.insert(tk.END, "\n\n" + self.explainer.get_history_string()) def what_if_dialog(self): """Öffnet Dialog für Was-wäre-wenn Simulation""" if not self.explainer: messagebox.showerror("Fehler", "Keine Analyse vorhanden!") return dialog = tk.Toplevel(self.root) dialog.title("Was-wäre-wenn Simulation") dialog.geometry("400x200") ttk.Label(dialog, text="Symbol:").grid(row=0, column=0, padx=5, pady=5) symbol_entry = ttk.Entry(dialog) symbol_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Label(dialog, text="Alternativer Code (5 Bit):").grid(row=1, column=0, padx=5, pady=5) code_entry = ttk.Entry(dialog) code_entry.grid(row=1, column=1, padx=5, pady=5) def simulate(): symbol = symbol_entry.get().strip() code = code_entry.get().strip() if symbol and code: explanation = self.explainer.what_if(symbol, code) self.text_xai.delete("1.0", tk.END) self.text_xai.insert(tk.END, explanation) dialog.destroy() ttk.Button(dialog, text="Simulieren", command=simulate).grid(row=2, column=0, columnspan=2, pady=20) def explain_rules(self): """Erklärt die gelernten Automaten-Regeln""" if not self.automaton or not self.automaton.transitions: messagebox.showerror("Fehler", "Kein Automat vorhanden!") return explanation = [] explanation.append("ERKLÄRUNG DER AUTOMATEN-REGELN") explanation.append("=" * 60) for (state, symbol), next_state in self.automaton.transitions.items(): conf = self.automaton.confidence_metrics.get((state, symbol), 0) explanation.append(f"\nRegel: {state} --({symbol})--> {next_state}") explanation.append(f" Konfidenz: {conf:.0%}") if conf > 0.7: explanation.append(" ✓ Diese Regel wurde in vielen Ketten konsistent beobachtet") elif conf > 0.4: explanation.append(" ⚠️ Diese Regel wurde mehrfach, aber nicht immer konsistent beobachtet") else: explanation.append(" ❌ Diese Regel basiert auf wenigen Beobachtungen") self.text_xai.delete("1.0", tk.END) self.text_xai.insert(tk.END, "\n".join(explanation)) def plot_coding_comparison(self): """Visualisiert den Kodierungsvergleich""" if not self.derivation_manager.results: messagebox.showerror("Fehler", "Keine Kodierungsergebnisse vorhanden!") return symbols = list(self.terminals)[:10] # Erste 10 Symbole self.visualizer.plot_coding_comparison(self.derivation_manager.results, symbols) def plot_confidence(self): """Visualisiert Konfidenzen""" if not self.automaton or not self.automaton.transitions: messagebox.showerror("Fehler", "Kein Automat vorhanden!") return self.visualizer.plot_confidence_heatmap(self.automaton) def plot_automaton(self): """Visualisiert den Automaten""" if not self.automaton or not self.automaton.transitions: messagebox.showerror("Fehler", "Kein Automat vorhanden!") return self.visualizer.plot_automaton_graph(self.automaton) def calculate_statistics(self): """Berechnet und zeigt Statistiken an""" if not self.chains: return stats = [] stats.append("STATISTISCHE KENNZAHLEN") stats.append("=" * 60) # Grundstatistiken chain_lengths = [len(chain) for chain in self.chains] stats.append(f"\nAnzahl Ketten: {len(self.chains)}") stats.append(f"Anzahl Terminale: {len(self.terminals)}") stats.append(f"Durchschnittliche Länge: {np.mean(chain_lengths):.1f}") stats.append(f"Minimale Länge: {min(chain_lengths)}") stats.append(f"Maximale Länge: {max(chain_lengths)}") # Häufigste Symbole symbol_counts = Counter() for chain in self.chains: symbol_counts.update(chain) stats.append("\nHäufigste Symbole:") for sym, count in symbol_counts.most_common(10): stats.append(f" {sym}: {count}x") # Kommentare if self.comments: stats.append(f"\nKommentare: {len(self.comments)}") self.text_stats.delete("1.0", tk.END) self.text_stats.insert(tk.END, "\n".join(stats)) def show_evolution(self): """Zeigt die Evolution der gelernten Strukturen""" evolution = self.learner.show_evolution() self.text_evolution.delete("1.0", tk.END) self.text_evolution.insert(tk.END, evolution) def show_export_dialog(self): """Zeigt Dialog für Export-Optionen""" if not self.derivation_manager.consensus_coding: messagebox.showerror("Fehler", "Keine Daten zum Exportieren!") return dialog = tk.Toplevel(self.root) dialog.title("Exportieren") dialog.geometry("300x250") export_data = { 'coding': self.derivation_manager.consensus_coding, 'terminals': list(self.terminals), 'chains': self.chains, 'comments': self.comments, 'timestamp': datetime.now().isoformat() } ttk.Label(dialog, text="Export-Format:").pack(pady=10) def export_json(): filepath = self.exporter.to_json(export_data) messagebox.showinfo("Export erfolgreich", f"Gespeichert als:\n{filepath}") dialog.destroy() def export_csv(): filepath = self.exporter.to_csv(export_data) messagebox.showinfo("Export erfolgreich", f"Gespeichert als:\n{filepath}") dialog.destroy() def export_html(): filepath = self.exporter.to_html(export_data) messagebox.showinfo("Export erfolgreich", f"Gespeichert als:\n{filepath}") dialog.destroy() def export_latex(): filepath = self.exporter.to_latex(export_data) messagebox.showinfo("Export erfolgreich", f"Gespeichert als:\n{filepath}") dialog.destroy() ttk.Button(dialog, text="JSON", command=export_json).pack(pady=5) ttk.Button(dialog, text="CSV", command=export_csv).pack(pady=5) ttk.Button(dialog, text="HTML (Bericht)", command=export_html).pack(pady=5) ttk.Button(dialog, text="LaTeX", command=export_latex).pack(pady=5) def load_file(self): """Lädt eine Datei""" filename = filedialog.askopenfilename( title="Datei auswählen", filetypes=[("Textdateien", "*.txt"), ("Alle Dateien", "*.*")] ) if filename: try: with open(filename, 'r', encoding='utf-8') as f: content = f.read() self.text_input.delete("1.0", tk.END) self.text_input.insert("1.0", content) self.status_var.set(f"Geladen: {filename}") self.parse_input() except Exception as e: messagebox.showerror("Fehler", f"Kann Datei nicht laden:\n{e}") def load_transcripts(self): """Alias für load_file""" self.load_file() def load_example(self): """Lädt ein Beispiel""" example = """# Beispieltranskripte für Verkaufsgespräche # Jede Zeile enthält eine Sequenz von Terminalzeichen # Transkript 1: Standard-Verkauf KBG, VBG, KBBd, VBBd, KBA, VBA, KBBd, VBBd, KBA, VAA, KAA, VAV, KAV # Transkript 2: Mit Wiederholungen in der Bedarfsphase KBG, VBG, KBBd, VBBd, KBBd, VBBd, KBA, VBA, VAA, KAA, VAV, KAV # Transkript 3: Kurzer Verkauf KBG, VBG, KBBd, VBBd, KBA, VBA, VAA, KAA, VAV, KAV # Transkript 4: Mit Beratungsphase KBG, VBG, KBBd, VBBd, KBA, VBA, KAE, VAE, KBA, VBA, VAA, KAA, VAV, KAV # Transkript 5: Mit vielen Bedarfswiederholungen KBG, VBG, KBBd, VBBd, KBBd, VBBd, KBBd, VBBd, KBA, VBA, VAA, KAA, VAV, KAV""" self.text_input.delete("1.0", tk.END) self.text_input.insert("1.0", example) self.parse_input() def show_module_status(self): """Zeigt den Status der optionalen Module""" status_text = "MODULSTATUS:\n" status_text += "=" * 40 + "\n" for module, available in MODULE_STATUS.items(): status = "✓ verfügbar" if available else "✗ nicht verfügbar" status_text += f"{module:15s}: {status}\n" messagebox.showinfo("Modulstatus", status_text) def show_about(self): """Zeigt Über-Informationen""" about = """ARSXAI7 - Algorithmic Recursive Sequence Analysis with Explainable AI Version 7.0 (Vollständige XAI-Integration) Kernfunktionen: • Universelle Analyse beliebiger Terminalzeichenketten • Mehrere Strategien zur automatischen Strukturableitung • Konfidenzmetriken für alle Ableitungen • Interaktive Erklärungskomponente ("Warum?") • Progressive Learning aus neuen Daten • Umfangreiche Visualisierungen • Export in verschiedene Formate © 2024 - Explainable AI Research""" messagebox.showinfo("Über ARSXAI7", about) # ============================================================================ # HAUPTFUNKTION # ============================================================================ def main(): """Hauptfunktion""" root = tk.Tk() app = ARSXAI7GUI(root) root.mainloop() if __name__ == "__main__": main()