import { Node, NodeType } from "../model/Argument"; import { CertusContext, FZ } from "../model/Context"; import { EvaluationError } from "../parser/Evaluator"; import { DeductiveMacro } from "./DeductiveMacro"; import { FuseStrictMacro } from "./FuseStrict"; import { NoChildrenMacroError } from "./Macro"; /** * Implements a fuse or averaging operation among the child nodes * in the certus context. Applies deductive reasoning. */ export class FuseMacro extends DeductiveMacro { constructor(name?: string | undefined) { super(name || "fuse"); } public evaluate(ctx: CertusContext): FZ { const root: Node = ctx.getRoot(); const nodes = this.getNodesFromContext(ctx); const P = nodes.filter(n => NodeType.checkSubType(NodeType.PREMISE, n.type)); const D = nodes.filter(n => NodeType.checkSubType(NodeType.DEFEATER, n.type)); if (P.length == 0 && D.length == 0) { return ctx.getDefaultCanonical(root.type); } else { const CS = ctx.getCanonicalSets(); const pScores = P.map(x => this.scoreNode(ctx, x, CS)); const dScores = D.map(x => this.scoreNode(ctx, x, CS)); let result: number = 0; if (NodeType.checkSubType(NodeType.PREMISE, root.type)) { if (P.length > 0) { const pSum = pScores.reduce((acc, x) => acc + x, 0); const dSum = dScores.reduce((acc, x) => acc + Math.max(0, x), 0); result = (pSum - dSum) / P.length; } else { result = -1 * dScores.reduce((acc, x) => acc + Math.max(0, x), 0); } } else if (NodeType.checkSubType(NodeType.DEFEATER, root.type)) { const dSum = dScores.reduce((acc, x) => acc + x, 0); const pSum = pScores.reduce((acc, x) => acc + Math.max(0, x), 0); result = (dSum - pSum) / (P.length + D.length); } else { throw new EvaluationError(`Unsupported parent node type ${root.type} when evaluating ${this.name} macro`) } return this.scoreToSet(result, CS); } } public expand(ctx: CertusContext, target?: string): string { const root: Node = ctx.getRoot(); const nodes = this.getNodesFromContext(ctx); const premises = nodes.filter(n => NodeType.checkSubType(NodeType.PREMISE, n.type)); const defeaters = nodes.filter(n => NodeType.checkSubType(NodeType.DEFEATER, n.type)); if (premises.length == 0 && defeaters.length == 0) { return `${target} is ${ctx.getDefaultCanonical(root.type).name}`; } else { const pNames = premises.map(n => n.id); const dNames = defeaters.map(n => n.id); const expr = this.generateCases(root.type, pNames, dNames, ctx.getCanonicalSets(), ctx.getCanonicalSet('reject')); return `${target} is ${expr}`; } } }