Inhalt
Aktueller Ordner:
ARS_ExplainableAIARSXAI7.py
"""
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("<!DOCTYPE html>")
html.append("<html>")
html.append("<head><title>ARSXAI7 Analysebericht</title>")
html.append("<style>")
html.append("body { font-family: Arial, sans-serif; margin: 40px; }")
html.append("table { border-collapse: collapse; width: 100%; }")
html.append("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }")
html.append("th { background-color: #4CAF50; color: white; }")
html.append("tr:nth-child(even) { background-color: #f2f2f2; }")
html.append(".confidence-high { color: green; }")
html.append(".confidence-medium { color: orange; }")
html.append(".confidence-low { color: red; }")
html.append("</style>")
html.append("</head>")
html.append("<body>")
html.append(f"<h1>ARSXAI7 Analysebericht</h1>")
html.append(f"<p>Generiert: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>")
if 'coding' in data:
html.append("<h2>Kodierungsergebnisse</h2>")
html.append("<table>")
html.append("<tr><th>Symbol</th><th>Code</th><th>Konfidenz</th></tr>")
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"<tr>")
html.append(f"<td>{symbol}</td>")
html.append(f"<td>{info.get('code', '')}</td>")
html.append(f"<td class='{conf_class}'>{conf:.0%}</td>")
html.append(f"</tr>")
html.append("</table>")
html.append("</body>")
html.append("</html>")
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()