355 lines
14 KiB
JavaScript
355 lines
14 KiB
JavaScript
|
|
/**
|
||
|
|
* Test Bilattice Integration with Existing Possibilistic Infrastructure
|
||
|
|
*
|
||
|
|
* This test demonstrates how bilattice orderings are integrated into our existing
|
||
|
|
* rule system while maintaining compatibility with possibilistic reasoning.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { test, describe } from 'node:test';
|
||
|
|
import assert from 'node:assert';
|
||
|
|
import {
|
||
|
|
QualitativeScale,
|
||
|
|
QualitativeCapacity,
|
||
|
|
BilatticeOrderings,
|
||
|
|
getSetKey
|
||
|
|
} from '../../src/qualitative/index.js';
|
||
|
|
|
||
|
|
describe('Bilattice Integration with Possibilistic Infrastructure', () => {
|
||
|
|
|
||
|
|
test('should demonstrate BaseRule bilattice-enhanced evidence combination', () => {
|
||
|
|
const scale = QualitativeScale.fivePoint();
|
||
|
|
const stateSpace = ['evidence1', 'evidence2', 'evidence3'];
|
||
|
|
|
||
|
|
// Create a capacity for bilattice analysis
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['evidence1'])), 0.75); // High belief evidence
|
||
|
|
qmt.set(getSetKey(new Set(['evidence2'])), 0.5); // Medium belief evidence
|
||
|
|
qmt.set(getSetKey(new Set(['evidence3'])), 0.25); // Low belief evidence
|
||
|
|
qmt.set(getSetKey(new Set(['evidence1', 'evidence2'])), 1); // Combined evidence
|
||
|
|
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
// Simulate collected values from rule evaluation - using valid fivePoint scale values
|
||
|
|
const collectedValues = [
|
||
|
|
{
|
||
|
|
value: 0.75, // Valid fivePoint scale value
|
||
|
|
possibility: 0.75, // Valid fivePoint scale value
|
||
|
|
path: ['user', 'relation1'],
|
||
|
|
source: { entityKey: 'user1', relation: 'score', step: 0 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.9 }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.5, // Valid fivePoint scale value
|
||
|
|
possibility: 0.5, // Valid fivePoint scale value
|
||
|
|
path: ['user', 'relation2'],
|
||
|
|
source: { entityKey: 'user2', relation: 'rating', step: 1 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.8 }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.25, // Valid fivePoint scale value
|
||
|
|
possibility: 0.25, // Valid fivePoint scale value
|
||
|
|
path: ['user', 'relation3'],
|
||
|
|
source: { entityKey: 'user3', relation: 'feedback', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.7 }
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
// Test information-based evidence selection
|
||
|
|
const epistemicPairs = collectedValues.map(cv => ({
|
||
|
|
belief: cv.possibility,
|
||
|
|
disbelief: 1 - cv.possibility
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Create propositions for bilattice analysis
|
||
|
|
const propositions = collectedValues.map((cv, index) => [`evidence${index + 1}`]);
|
||
|
|
|
||
|
|
const mostInformative = BilatticeOrderings.findMostInformative(propositions, capacity);
|
||
|
|
|
||
|
|
// The most informative evidence should be the one with highest belief AND disbelief
|
||
|
|
// Evidence 1: belief=0.75, disbelief=0.25
|
||
|
|
// Evidence 2: belief=0.5, disbelief=0.5
|
||
|
|
// Evidence 3: belief=0.25, disbelief=0.75
|
||
|
|
// Evidence 1 and 2 are incomparable in information ordering (neither dominates)
|
||
|
|
// The algorithm picks the first one (evidence 1) as default
|
||
|
|
assert.strictEqual(mostInformative.epistemic.belief, 0.75);
|
||
|
|
// Disbelief = capacity of the complement: max focal subset of
|
||
|
|
// {evidence2, evidence3} is {evidence2}=0.5.
|
||
|
|
assert.strictEqual(mostInformative.epistemic.disbelief, 0.5);
|
||
|
|
assert.strictEqual(mostInformative.rank, 1);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should demonstrate qualitative relational comparator with bilattice reasoning', () => {
|
||
|
|
const scale = QualitativeScale.fivePoint();
|
||
|
|
|
||
|
|
// Simulate blurred values from qualitative decay
|
||
|
|
const blurredValues = [
|
||
|
|
{
|
||
|
|
interval: { lower: 0.5, upper: 0.75 },
|
||
|
|
possibility: 0.75,
|
||
|
|
originalValue: 0.75,
|
||
|
|
originalPossibility: 1.0,
|
||
|
|
timestamp: Date.now(),
|
||
|
|
relation: 'score',
|
||
|
|
meta: { periodsElapsed: 1, blurSteps: 1 }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
interval: { lower: 0.25, upper: 0.5 },
|
||
|
|
possibility: 0.5,
|
||
|
|
originalValue: 0.5,
|
||
|
|
originalPossibility: 0.8,
|
||
|
|
timestamp: Date.now(),
|
||
|
|
relation: 'rating',
|
||
|
|
meta: { periodsElapsed: 2, blurSteps: 2 }
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
// Test epistemic comparison of blurred values
|
||
|
|
const epistemicPairs = blurredValues.map(bv => ({
|
||
|
|
belief: bv.possibility,
|
||
|
|
disbelief: 1 - bv.possibility
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Create a simple capacity for comparison
|
||
|
|
const stateSpace = ['blurred1', 'blurred2'];
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['blurred1'])), 0.75);
|
||
|
|
qmt.set(getSetKey(new Set(['blurred2'])), 0.5);
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
const comparison = BilatticeOrderings.compareEpistemicStatus(
|
||
|
|
['blurred1'], ['blurred2'], capacity
|
||
|
|
);
|
||
|
|
|
||
|
|
// blurred1 should be more true than blurred2 (higher capacity value)
|
||
|
|
assert.ok(comparison.truthOrdering);
|
||
|
|
assert.strictEqual(comparison.relationship, 'A more true than B');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should demonstrate defeasible logic with bilattice reasoning', () => {
|
||
|
|
const scale = QualitativeScale.tenPoint();
|
||
|
|
|
||
|
|
// Simulate evidence from different rule types in defeasible logic
|
||
|
|
const strictEvidence = {
|
||
|
|
value: 1.0,
|
||
|
|
possibility: 1.0,
|
||
|
|
path: ['strict_rule'],
|
||
|
|
source: { entityKey: 'system', relation: 'strict_check', step: 0 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 1.0, ruleType: 'strict' }
|
||
|
|
};
|
||
|
|
|
||
|
|
const defeasibleEvidence = {
|
||
|
|
value: 0.8,
|
||
|
|
possibility: 0.8,
|
||
|
|
path: ['defeasible_rule'],
|
||
|
|
source: { entityKey: 'user', relation: 'user_check', step: 1 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.9, ruleType: 'defeasible' }
|
||
|
|
};
|
||
|
|
|
||
|
|
const defeaterEvidence = {
|
||
|
|
value: 0.6,
|
||
|
|
possibility: 0.6,
|
||
|
|
path: ['defeater_rule'],
|
||
|
|
source: { entityKey: 'security', relation: 'security_check', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.8, ruleType: 'defeater' }
|
||
|
|
};
|
||
|
|
|
||
|
|
const allEvidence = [strictEvidence, defeasibleEvidence, defeaterEvidence];
|
||
|
|
|
||
|
|
// Create capacity representing the defeasible logic structure
|
||
|
|
const stateSpace = ['strict', 'defeasible', 'defeater'];
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['strict'])), 1.0); // Strict rules have highest priority
|
||
|
|
qmt.set(getSetKey(new Set(['defeasible'])), 0.8); // Defeasible rules have medium priority
|
||
|
|
qmt.set(getSetKey(new Set(['defeater'])), 0.6); // Defeaters have lower priority
|
||
|
|
qmt.set(getSetKey(new Set(['strict', 'defeasible'])), 1.0); // Strict + defeasible = strict wins
|
||
|
|
qmt.set(getSetKey(new Set(['strict', 'defeater'])), 1.0); // Strict + defeater = strict wins
|
||
|
|
qmt.set(getSetKey(new Set(['defeasible', 'defeater'])), 0.8); // Defeasible + defeater = defeasible wins
|
||
|
|
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
// Test information ordering for defeasible logic
|
||
|
|
const epistemicPairs = allEvidence.map(ev => ({
|
||
|
|
belief: ev.possibility,
|
||
|
|
disbelief: 1 - ev.possibility
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Create propositions for bilattice analysis (must match the capacity state space)
|
||
|
|
const propositions = [['strict'], ['defeasible'], ['defeater']];
|
||
|
|
|
||
|
|
const informationRanking = BilatticeOrderings.rankByInformation(propositions, capacity);
|
||
|
|
|
||
|
|
// Strict evidence should rank highest in information ordering
|
||
|
|
const strictRank = informationRanking.find(r => r.epistemic.belief === 1.0)?.rank;
|
||
|
|
const defeasibleRank = informationRanking.find(r => r.epistemic.belief === 0.8)?.rank;
|
||
|
|
const defeaterRank = informationRanking.find(r => r.epistemic.belief === 0.6)?.rank;
|
||
|
|
|
||
|
|
assert.ok(strictRank <= defeasibleRank);
|
||
|
|
assert.ok(defeasibleRank <= defeaterRank);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should demonstrate chain rule with epistemic path analysis', () => {
|
||
|
|
const scale = QualitativeScale.tenPoint();
|
||
|
|
|
||
|
|
// Simulate collected values from a chain traversal
|
||
|
|
const chainValues = [
|
||
|
|
{
|
||
|
|
value: 0.9,
|
||
|
|
possibility: 0.9,
|
||
|
|
path: ['user', 'account', 'balance'],
|
||
|
|
source: { entityKey: 'account1', relation: 'balance', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.95, pathPossibility: 0.9 }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.7,
|
||
|
|
possibility: 0.7,
|
||
|
|
path: ['user', 'account', 'credit'],
|
||
|
|
source: { entityKey: 'account2', relation: 'credit', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.85, pathPossibility: 0.7 }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.5,
|
||
|
|
possibility: 0.5,
|
||
|
|
path: ['user', 'account', 'debt'],
|
||
|
|
source: { entityKey: 'account3', relation: 'debt', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.75, pathPossibility: 0.5 }
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
// Create capacity for path-based epistemic analysis
|
||
|
|
const stateSpace = ['path1', 'path2', 'path3'];
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['path1'])), 0.9); // High confidence path
|
||
|
|
qmt.set(getSetKey(new Set(['path2'])), 0.7); // Medium confidence path
|
||
|
|
qmt.set(getSetKey(new Set(['path3'])), 0.5); // Low confidence path
|
||
|
|
qmt.set(getSetKey(new Set(['path1', 'path2'])), 1.0); // Combined high-confidence paths
|
||
|
|
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
// Test truth ordering for path selection
|
||
|
|
const pathPropositions = [
|
||
|
|
['path1'], ['path2'], ['path3']
|
||
|
|
];
|
||
|
|
|
||
|
|
const truthRanking = BilatticeOrderings.rankByTruth(pathPropositions, capacity);
|
||
|
|
|
||
|
|
// Path1 should rank highest in truth ordering (highest capacity value)
|
||
|
|
const path1Rank = truthRanking.find(r => r.capacity === 0.9)?.rank;
|
||
|
|
const path2Rank = truthRanking.find(r => r.capacity === 0.7)?.rank;
|
||
|
|
const path3Rank = truthRanking.find(r => r.capacity === 0.5)?.rank;
|
||
|
|
|
||
|
|
assert.strictEqual(path1Rank, 1);
|
||
|
|
assert.ok(path2Rank > path1Rank);
|
||
|
|
assert.ok(path3Rank > path2Rank);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should demonstrate hybrid epistemic reasoning', () => {
|
||
|
|
const scale = QualitativeScale.tenPoint();
|
||
|
|
|
||
|
|
// Simulate mixed evidence from different sources
|
||
|
|
const mixedEvidence = [
|
||
|
|
{
|
||
|
|
value: 0.8,
|
||
|
|
possibility: 0.8,
|
||
|
|
path: ['direct_evidence'],
|
||
|
|
source: { entityKey: 'direct', relation: 'observation', step: 0 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.9, sourceType: 'direct' }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.6,
|
||
|
|
possibility: 0.6,
|
||
|
|
path: ['inferred_evidence'],
|
||
|
|
source: { entityKey: 'inference', relation: 'deduction', step: 1 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.7, sourceType: 'inferred' }
|
||
|
|
},
|
||
|
|
{
|
||
|
|
value: 0.4,
|
||
|
|
possibility: 0.4,
|
||
|
|
path: ['similarity_evidence'],
|
||
|
|
source: { entityKey: 'similarity', relation: 'analogy', step: 2 },
|
||
|
|
metadata: { timestamp: Date.now(), reliability: 0.6, sourceType: 'similarity' }
|
||
|
|
}
|
||
|
|
];
|
||
|
|
|
||
|
|
// Create capacity for hybrid analysis
|
||
|
|
const stateSpace = ['direct', 'inferred', 'similarity'];
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['direct'])), 0.8); // Direct evidence has highest reliability
|
||
|
|
qmt.set(getSetKey(new Set(['inferred'])), 0.6); // Inferred evidence has medium reliability
|
||
|
|
qmt.set(getSetKey(new Set(['similarity'])), 0.4); // Similarity evidence has lowest reliability
|
||
|
|
qmt.set(getSetKey(new Set(['direct', 'inferred'])), 0.9); // Direct + inferred = high confidence
|
||
|
|
qmt.set(getSetKey(new Set(['direct', 'similarity'])), 0.8); // Direct + similarity = direct dominates
|
||
|
|
qmt.set(getSetKey(new Set(['inferred', 'similarity'])), 0.6); // Inferred + similarity = inferred dominates
|
||
|
|
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
// Test comprehensive epistemic comparison
|
||
|
|
const epistemicPairs = mixedEvidence.map(ev => ({
|
||
|
|
belief: ev.possibility,
|
||
|
|
disbelief: 1 - ev.possibility
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Create propositions for bilattice analysis (must match the capacity state space)
|
||
|
|
const propositions = [['direct'], ['inferred'], ['similarity']];
|
||
|
|
|
||
|
|
const informationRanking = BilatticeOrderings.rankByInformation(propositions, capacity);
|
||
|
|
const truthRanking = BilatticeOrderings.rankByTruth(propositions, capacity);
|
||
|
|
|
||
|
|
// Direct evidence should rank highest in both orderings
|
||
|
|
const directInfoRank = informationRanking.find(r => r.epistemic.belief === 0.8)?.rank;
|
||
|
|
const directTruthRank = truthRanking.find(r => r.capacity === 0.8)?.rank;
|
||
|
|
|
||
|
|
assert.strictEqual(directInfoRank, 1);
|
||
|
|
assert.strictEqual(directTruthRank, 1);
|
||
|
|
|
||
|
|
// Test comprehensive comparison
|
||
|
|
const comparison = BilatticeOrderings.compareEpistemicStatus(
|
||
|
|
['direct'], ['inferred'], capacity
|
||
|
|
);
|
||
|
|
|
||
|
|
assert.ok(comparison.truthOrdering);
|
||
|
|
assert.strictEqual(comparison.relationship, 'A more true than B');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should maintain backward compatibility with existing possibilistic infrastructure', () => {
|
||
|
|
const scale = QualitativeScale.tenPoint();
|
||
|
|
|
||
|
|
// Test that bilattice reasoning can be disabled and standard OWA fusion still works
|
||
|
|
const standardEvidence = [
|
||
|
|
{ value: 0.8, possibility: 0.8, path: ['evidence1'], source: {}, metadata: {} },
|
||
|
|
{ value: 0.6, possibility: 0.6, path: ['evidence2'], source: {}, metadata: {} },
|
||
|
|
{ value: 0.4, possibility: 0.4, path: ['evidence3'], source: {}, metadata: {} }
|
||
|
|
];
|
||
|
|
|
||
|
|
// Test standard OWA fusion (bilattice disabled)
|
||
|
|
const epistemicPairs = standardEvidence.map(ev => ({
|
||
|
|
belief: ev.possibility,
|
||
|
|
disbelief: 1 - ev.possibility
|
||
|
|
}));
|
||
|
|
|
||
|
|
// Standard max operation should select the highest value
|
||
|
|
const maxValue = Math.max(...standardEvidence.map(ev => ev.possibility));
|
||
|
|
assert.strictEqual(maxValue, 0.8);
|
||
|
|
|
||
|
|
// Test that bilattice reasoning can be enabled when needed
|
||
|
|
const stateSpace = ['ev1', 'ev2', 'ev3'];
|
||
|
|
const qmt = new Map();
|
||
|
|
qmt.set(getSetKey(new Set(['ev1'])), 0.8);
|
||
|
|
qmt.set(getSetKey(new Set(['ev2'])), 0.6);
|
||
|
|
qmt.set(getSetKey(new Set(['ev3'])), 0.4);
|
||
|
|
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
|
||
|
|
|
||
|
|
// Create propositions for bilattice analysis
|
||
|
|
const propositions = standardEvidence.map((ev, index) => [`ev${index + 1}`]);
|
||
|
|
|
||
|
|
const mostInformative = BilatticeOrderings.findMostInformative(propositions, capacity);
|
||
|
|
|
||
|
|
// Epistemic pairs from the capacity:
|
||
|
|
// ev1 ({ev1}): belief=0.8, disbelief=gamma({ev2,ev3})=0.6
|
||
|
|
// ev2 ({ev2}): belief=0.6, disbelief=gamma({ev1,ev3})=0.8
|
||
|
|
// ev3 ({ev3}): belief=0.4, disbelief=gamma({ev1,ev2})=0.8
|
||
|
|
// ev1 dominates in belief; the algorithm keeps the first best (ev1).
|
||
|
|
assert.strictEqual(mostInformative.epistemic.belief, 0.8);
|
||
|
|
assert.strictEqual(mostInformative.epistemic.disbelief, 0.6);
|
||
|
|
});
|
||
|
|
});
|