/** * Test Unified Evidence Fusion System * * This test demonstrates the new separation of concerns between aggregation * (OWA) and reconciliation (bilattice/Dempster-Shafer) logic, supporting * both qualitative and quantitative modes with the same lexicon. */ import { test, describe } from 'node:test'; import assert from 'node:assert'; import { QualitativeScale, UnifiedEvidenceFusion, EvidenceAggregation, EvidenceReconciliation, NumericBilatticeOrderings } from '../../src/qualitative/index.js'; describe('Unified Evidence Fusion System', () => { test('should demonstrate aggregation vs reconciliation separation', () => { // Test data with conflicting evidence const collectedValues = [ { value: 0.8, possibility: 0.9, path: ['evidence1'], source: { type: 'direct', confidence: 0.95 }, metadata: { timestamp: Date.now(), reliability: 0.9 } }, { value: 0.3, possibility: 0.4, path: ['evidence2'], source: { type: 'inferred', confidence: 0.6 }, metadata: { timestamp: Date.now(), reliability: 0.7 } }, { value: 0.7, possibility: 0.6, path: ['evidence3'], source: { type: 'derived', confidence: 0.8 }, metadata: { timestamp: Date.now(), reliability: 0.8 } } ]; // Test pure aggregation (no reconciliation) const aggregationResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', aggregationMethod: 'majority', useReconciliation: false }); assert.ok(aggregationResult.hasValue); assert.strictEqual(aggregationResult.fusionMethod, 'aggregation'); assert.strictEqual(aggregationResult.reconciliationMethod, 'none'); assert.ok(aggregationResult.epistemicAnalysis === null); // Test reconciliation-based fusion const reconciliationResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', aggregationMethod: 'max', reconciliationMethod: 'dempster_shafer', epistemicMode: 'hybrid', useReconciliation: true }); assert.ok(reconciliationResult.hasValue); assert.strictEqual(reconciliationResult.fusionMethod, 'reconciliation'); assert.strictEqual(reconciliationResult.reconciliationMethod, 'dempster_shafer'); assert.ok(reconciliationResult.epistemicAnalysis !== null); }); test('should support both qualitative and quantitative modes with same lexicon', () => { const collectedValues = [ { value: 0.75, // Valid fivePoint scale value possibility: 0.75, path: ['evidence1'], source: { type: 'direct' }, metadata: { reliability: 0.9 } }, { value: 0.5, // Valid fivePoint scale value possibility: 0.5, path: ['evidence2'], source: { type: 'inferred' }, metadata: { reliability: 0.7 } } ]; const scale = QualitativeScale.fivePoint(); // Test quantitative mode const quantitativeResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', aggregationMethod: 'average', useReconciliation: false }); // Test qualitative mode const qualitativeResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'qualitative', aggregationMethod: 'average', scale: scale, useReconciliation: false }); assert.ok(quantitativeResult.hasValue); assert.ok(qualitativeResult.hasValue); assert.strictEqual(quantitativeResult.aggregationMethod, 'average'); assert.strictEqual(qualitativeResult.aggregationMethod, 'average'); // Both should use the same aggregation lexicon assert.ok(quantitativeResult.value > 0); assert.ok(qualitativeResult.value > 0); }); test('should demonstrate numeric bilattice orderings', () => { // Create a simple numeric capacity const capacity = { stateSpace: ['evidence1', 'evidence2', 'evidence3'], getCapacity: (set) => { // For single elements if (set.size === 1) { if (set.has('evidence1')) return 0.8; if (set.has('evidence2')) return 0.6; if (set.has('evidence3')) return 0.4; } // For complements (multiple elements) if (set.size === 2) { return 0.2; // Some belief in complements } // For empty set if (set.size === 0) { return 0; } // For full set if (set.size === 3) { return 1.0; } return 0; } }; const propositions = [['evidence1'], ['evidence2'], ['evidence3']]; // Test information ordering const mostInformative = NumericBilatticeOrderings.findMostInformative(propositions, capacity); assert.ok(mostInformative); assert.strictEqual(mostInformative.rank, 1); // Test truth ordering const mostTrue = NumericBilatticeOrderings.findMostTrue(propositions, capacity); assert.ok(mostTrue); assert.strictEqual(mostTrue.rank, 1); // Test Dempster-Shafer measures const belief = NumericBilatticeOrderings.dempsterShaferBelief(['evidence1'], capacity); const plausibility = NumericBilatticeOrderings.dempsterShaferPlausibility(['evidence1'], capacity); const uncertainty = NumericBilatticeOrderings.dempsterShaferUncertainty(['evidence1'], capacity); assert.strictEqual(belief, 0.8); // Plausibility should be 1 - capacity of complement // Complement of ['evidence1'] is ['evidence2', 'evidence3'] // Capacity of ['evidence2', 'evidence3'] is 0.2 // So plausibility = 1 - 0.2 = 0.8 assert.strictEqual(plausibility, 0.8); assert.strictEqual(uncertainty, 0); // 0.8 - 0.8 = 0 }); test('should demonstrate reconciliation methods comparison', () => { const collectedValues = [ { value: 0.8, possibility: 0.9, path: ['evidence1'], source: { type: 'direct' }, metadata: { reliability: 0.9 } }, { value: 0.3, possibility: 0.4, path: ['evidence2'], source: { type: 'inferred' }, metadata: { reliability: 0.7 } } ]; // Test different reconciliation methods const bilatticeResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', reconciliationMethod: 'bilattice', epistemicMode: 'hybrid', useReconciliation: true }); const dempsterShaferResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', reconciliationMethod: 'dempster_shafer', epistemicMode: 'hybrid', useReconciliation: true }); const subjectiveLogicResult = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', reconciliationMethod: 'subjective_logic', epistemicMode: 'hybrid', useReconciliation: true }); // All should produce valid results assert.ok(bilatticeResult.hasValue); assert.ok(dempsterShaferResult.hasValue); assert.ok(subjectiveLogicResult.hasValue); // All should have epistemic analysis assert.ok(bilatticeResult.epistemicAnalysis); assert.ok(dempsterShaferResult.epistemicAnalysis); assert.ok(subjectiveLogicResult.epistemicAnalysis); // Different methods may produce different results console.log('Bilattice result:', bilatticeResult.value); console.log('Dempster-Shafer result:', dempsterShaferResult.value); console.log('Subjective Logic result:', subjectiveLogicResult.value); }); test('should demonstrate aggregation methods comparison', () => { const collectedValues = [ { value: 0.8, possibility: 0.8, path: ['evidence1'], source: { type: 'direct' }, metadata: { reliability: 0.9 } }, { value: 0.6, possibility: 0.6, path: ['evidence2'], source: { type: 'inferred' }, metadata: { reliability: 0.7 } }, { value: 0.4, possibility: 0.4, path: ['evidence3'], source: { type: 'derived' }, metadata: { reliability: 0.8 } } ]; const aggregationMethods = ['max', 'min', 'average', 'majority', 'median']; const results = {}; for (const method of aggregationMethods) { results[method] = UnifiedEvidenceFusion.fuse(collectedValues, { mode: 'quantitative', aggregationMethod: method, useReconciliation: false }); } // All methods should produce valid results for (const [method, result] of Object.entries(results)) { assert.ok(result.hasValue, `Method ${method} should produce valid result`); assert.strictEqual(result.aggregationMethod, method); assert.ok(result.value >= 0 && result.value <= 1); } // Different methods should produce different results assert.ok(results.max.value >= results.average.value); assert.ok(results.average.value >= results.min.value); }); test('should demonstrate method comparison functionality', () => { const collectedValues = [ { value: 0.7, possibility: 0.7, path: ['evidence1'], source: { type: 'direct' }, metadata: { reliability: 0.9 } }, { value: 0.5, possibility: 0.5, path: ['evidence2'], source: { type: 'inferred' }, metadata: { reliability: 0.7 } } ]; const comparison = UnifiedEvidenceFusion.compareMethods(collectedValues, { mode: 'quantitative', aggregationMethods: ['max', 'average', 'majority'], reconciliationMethods: ['none', 'dempster_shafer'], epistemicModes: ['hybrid'] }); assert.ok(comparison.results); assert.ok(comparison.summary); assert.ok(comparison.summary.bestMethods.length > 0); assert.ok(comparison.summary.worstMethods.length > 0); assert.ok(comparison.summary.valueRange.min <= comparison.summary.valueRange.max); }); test('should validate fusion options', () => { const validation = UnifiedEvidenceFusion.validateOptions({ mode: 'quantitative', aggregationMethod: 'max', reconciliationMethod: 'bilattice', epistemicMode: 'hybrid', capacityType: 'simple_support' }); assert.ok(validation.valid); assert.strictEqual(validation.errors.length, 0); // Test invalid options const invalidValidation = UnifiedEvidenceFusion.validateOptions({ mode: 'invalid', aggregationMethod: 'invalid', reconciliationMethod: 'invalid' }); assert.ok(!invalidValidation.valid); assert.ok(invalidValidation.errors.length > 0); }); test('should get available methods and descriptions', () => { const availableMethods = UnifiedEvidenceFusion.getAvailableMethods(); assert.ok(availableMethods.aggregation); assert.ok(availableMethods.reconciliation); assert.ok(availableMethods.epistemicModes); assert.ok(availableMethods.capacityTypes); const descriptions = UnifiedEvidenceFusion.getMethodDescriptions(); assert.ok(descriptions.aggregation); assert.ok(descriptions.reconciliation); assert.ok(descriptions.epistemicModes); assert.ok(descriptions.capacityTypes); // Test specific descriptions assert.ok(descriptions.aggregation.max); assert.ok(descriptions.reconciliation.bilattice); assert.ok(descriptions.epistemicModes.hybrid); }); });