/**
* ASP Visualizer
* Generate visual representations of ASP programs and solutions
*/
import { ASPProgram, ASPSolution, AnswerSet, ASPAtom } from '../../../types.js';
export class ASPVisualizer {
/**
* Visualize ASP program structure
*/
visualizeProgram(program: ASPProgram): string {
let viz = '=== ASP Program Structure ===\n\n';
viz += `Facts: ${program.facts.length}\n`;
viz += `Rules: ${program.rules.length}\n`;
viz += `Constraints: ${program.constraints.length}\n`;
if (program.weakConstraints && program.weakConstraints.length > 0) {
viz += `Weak Constraints: ${program.weakConstraints.length}\n`;
}
if (program.choices && program.choices.length > 0) {
viz += `Choice Rules: ${program.choices.length}\n`;
}
viz += '\n';
// Dependency graph
viz += this.visualizeDependencyGraph(program);
return viz;
}
/**
* Visualize dependency graph
*/
private visualizeDependencyGraph(program: ASPProgram): string {
let viz = '--- Dependency Graph ---\n\n';
const dependencies: Map<string, Set<string>> = new Map();
// Build dependency graph
for (const rule of program.rules) {
const headPreds = Array.isArray(rule.head)
? rule.head.map(h => h.predicate)
: [rule.head.predicate];
for (const headPred of headPreds) {
if (!dependencies.has(headPred)) {
dependencies.set(headPred, new Set());
}
for (const bodyLit of rule.body) {
dependencies.get(headPred)!.add(bodyLit.atom.predicate);
}
}
}
// Display dependencies
for (const [pred, deps] of dependencies) {
viz += `${pred}`;
if (deps.size > 0) {
viz += ` ← ${Array.from(deps).join(', ')}`;
}
viz += '\n';
}
return viz;
}
/**
* Visualize answer sets
*/
visualizeSolution(solution: ASPSolution): string {
if (!solution.satisfiable) {
return '=== UNSATISFIABLE ===\n\nNo answer sets exist.';
}
let viz = `=== ${solution.totalModels} Answer Set(s) ===\n\n`;
for (const answerSet of solution.answerSets) {
viz += this.visualizeAnswerSet(answerSet) + '\n\n';
}
return viz.trim();
}
/**
* Visualize single answer set
*/
private visualizeAnswerSet(answerSet: AnswerSet): string {
let viz = `Answer Set ${answerSet.id}`;
if (answerSet.isOptimal) {
viz += ' (OPTIMAL)';
}
if (answerSet.cost) {
viz += ` [Cost: ${answerSet.cost.join(', ')}]`;
}
viz += '\n';
// Group by predicate
const grouped = this.groupByPredicate(answerSet.atoms);
for (const [pred, atoms] of Object.entries(grouped)) {
viz += ` ${pred}:`;
for (const atom of atoms) {
viz += ` (${atom.terms.join(',')})`;
}
viz += '\n';
}
return viz;
}
/**
* Visualize comparison between answer sets
*/
visualizeComparison(as1: AnswerSet, as2: AnswerSet): string {
let viz = `=== Comparison: AS ${as1.id} vs AS ${as2.id} ===\n\n`;
const atoms1 = new Set(as1.atoms.map(a => this.atomKey(a)));
const atoms2 = new Set(as2.atoms.map(a => this.atomKey(a)));
const common = [...atoms1].filter(a => atoms2.has(a));
const only1 = [...atoms1].filter(a => !atoms2.has(a));
const only2 = [...atoms2].filter(a => !atoms1.has(a));
viz += `Common: ${common.length} atoms\n`;
viz += `Only in AS ${as1.id}: ${only1.length} atoms\n`;
viz += `Only in AS ${as2.id}: ${only2.length} atoms\n\n`;
if (only1.length > 0) {
viz += `Differences in AS ${as1.id}:\n`;
for (const atom of only1.slice(0, 10)) {
viz += ` + ${atom}\n`;
}
if (only1.length > 10) {
viz += ` ... and ${only1.length - 10} more\n`;
}
viz += '\n';
}
if (only2.length > 0) {
viz += `Differences in AS ${as2.id}:\n`;
for (const atom of only2.slice(0, 10)) {
viz += ` + ${atom}\n`;
}
if (only2.length > 10) {
viz += ` ... and ${only2.length - 10} more\n`;
}
}
return viz;
}
/**
* Visualize derivation tree
*/
visualizeDerivation(atom: string, depth: number = 0): string {
const indent = ' '.repeat(depth);
return `${indent}${atom}\n`;
}
/**
* Group atoms by predicate
*/
private groupByPredicate(atoms: ASPAtom[]): Record<string, ASPAtom[]> {
const grouped: Record<string, ASPAtom[]> = {};
for (const atom of atoms) {
if (!grouped[atom.predicate]) {
grouped[atom.predicate] = [];
}
grouped[atom.predicate].push(atom);
}
return grouped;
}
/**
* Create unique key for atom
*/
private atomKey(atom: ASPAtom): string {
return `${atom.predicate}(${atom.terms.join(',')})`;
}
}