import { BaseNode } from './BaseNode.js'; /** * AST node for fusion statements * Represents: fusion max { ... } */ export class FusionNode extends BaseNode { constructor(strategy, location = null) { super('Fusion', location); this.strategy = strategy; // 'max', 'min', 'majority', 'average', etc. this.evidence = []; // Array of evidence statements this.weights = null; // Optional weights array } /** * Add evidence to this fusion * @param {BaseNode} evidence - Evidence to add */ addEvidence(evidence) { this.evidence.push(evidence); this.addChild(evidence); } /** * Set weights for this fusion * @param {number[]} weights - Weights array */ setWeights(weights) { this.weights = weights; } /** * Get the fusion strategy * @returns {string} Fusion strategy */ getStrategy() { return this.strategy; } /** * Get all evidence statements * @returns {BaseNode[]} Evidence statements */ getEvidence() { return this.evidence; } /** * Get the weights for this fusion * @returns {number[]|null} Weights or null */ getWeights() { return this.weights; } /** * Check if this fusion has weights * @returns {boolean} True if has weights */ hasWeights() { return this.weights !== null && this.weights.length > 0; } /** * Check if this is a max fusion * @returns {boolean} True if max fusion */ isMax() { return this.strategy === 'max'; } /** * Check if this is a min fusion * @returns {boolean} True if min fusion */ isMin() { return this.strategy === 'min'; } /** * Check if this is a majority fusion * @returns {boolean} True if majority fusion */ isMajority() { return this.strategy === 'majority'; } /** * Check if this is an average fusion * @returns {boolean} True if average fusion */ isAverage() { return this.strategy === 'average'; } /** * Get the number of evidence statements * @returns {number} Number of evidence statements */ getEvidenceCount() { return this.evidence.length; } /** * Validate the fusion * @returns {string[]} Array of error messages */ validate() { const errors = []; // Validate strategy const validStrategies = [ 'max', 'min', 'majority', 'average', 'sum', 'sum_unbounded', 'median', 'optimistic', 'pessimistic', 'top2', 'top3', 'priority', 'custom', 'count' ]; if (!validStrategies.includes(this.strategy)) { errors.push(`Invalid fusion strategy: ${this.strategy}`); } // Validate evidence if (this.evidence.length === 0) { errors.push('Fusion must have at least one evidence statement'); } // Validate each evidence statement this.evidence.forEach((ev, index) => { const evErrors = ev.validate ? ev.validate() : []; errors.push(...evErrors.map(err => `Evidence ${index + 1}: ${err}`)); }); // Validate weights if (this.weights !== null) { if (!Array.isArray(this.weights)) { errors.push('Weights must be an array'); } else if (this.weights.length !== this.evidence.length) { errors.push('Weights array length must match evidence count'); } else if (this.weights.some(w => typeof w !== 'number' || w < 0)) { errors.push('All weights must be non-negative numbers'); } else if (this.strategy === 'custom') { const total = this.weights.reduce((sum, w) => sum + w, 0); if (Math.abs(total - 1.0) > 1e-6) { errors.push('Custom weights must sum to 1.0'); } } } if (this.strategy === 'custom' && (!this.weights || this.weights.length === 0)) { errors.push('Custom fusion requires weights'); } return errors; } toString() { const weightsStr = this.hasWeights() ? ` weights[${this.weights.length}]` : ''; return `Fusion(${this.strategy}, ${this.evidence.length} evidence${weightsStr})`; } }