Skip to main content
Glama

Narrative Graph MCP

rtm-math.ts5.63 kB
/** * Mathematical utilities for Random Tree Model calculations * @module core/rtm-math */ import { TemporalScale, RTMParameters } from '../types/rtm.js'; /** * Calculate the number of weak compositions of n into k parts * This is the count of ways to partition n items into k bins (allowing empty bins) * @param n - Total number to partition * @param k - Number of parts/bins * @returns Number of weak compositions */ export function weakCompositions(n: number, k: number): number { if (k === 0) return n === 0 ? 1 : 0; if (k === 1) return 1; // Using stars and bars: C(n+k-1, k-1) return binomialCoefficient(n + k - 1, k - 1); } /** * Calculate binomial coefficient C(n, k) * @param n - Total items * @param k - Items to choose * @returns Binomial coefficient */ export function binomialCoefficient(n: number, k: number): number { if (k > n) return 0; if (k === 0 || k === n) return 1; // Optimize by using smaller k k = Math.min(k, n - k); let result = 1; for (let i = 0; i < k; i++) { result = (result * (n - i)) / (i + 1); } return Math.round(result); } /** * Calculate the probability of obtaining child node sizes from a parent * Based on the RTM recurrence relation * @param parentSize - Size of parent node * @param childSizes - Array of child node sizes * @param maxBranching - Maximum branching factor K * @returns Probability of this partition */ export function partitionProbability( parentSize: number, childSizes: number[], maxBranching: number ): number { const k = childSizes.length; if (k > maxBranching || k === 0) return 0; const childSum = childSizes.reduce((sum, size) => sum + size, 0); if (childSum !== parentSize) return 0; // P(n^(l+1)|n^(l)) = Z_K(n^(l)) / Z_{K-1}(n^(l) - n^(l+1)) const numerator = weakCompositions(parentSize - childSum, maxBranching - 1); const denominator = weakCompositions(parentSize, maxBranching); return numerator / denominator; } /** * Calculate compression ratio for a given temporal scale * @param nodeSize - Number of clauses in the node * @param scale - Temporal scale * @param parameters - RTM parameters * @returns Compression ratio */ export function calculateCompressionRatio( nodeSize: number, scale: TemporalScale, parameters: RTMParameters ): number { const targetCompression = parameters.compressionTargets[scale]; // Base compression increases with node size const baseCompression = Math.min(nodeSize / 10, targetCompression); // Apply scale-specific adjustment const scaleMultiplier = getScaleMultiplier(scale); return baseCompression * scaleMultiplier; } /** * Get the hierarchical depth multiplier for a temporal scale * @param scale - Temporal scale * @returns Scale multiplier */ function getScaleMultiplier(scale: TemporalScale): number { const multipliers: Record<TemporalScale, number> = { 'clause': 1.0, 'sentence': 1.2, 'paragraph': 1.5, 'section': 2.0, 'chapter': 3.0, 'document': 4.0 }; return multipliers[scale] || 1.0; } /** * Calculate the expected recall length given narrative length and parameters * @param narrativeLength - Total number of clauses * @param parameters - RTM parameters * @returns Expected recall length */ export function expectedRecallLength( narrativeLength: number, parameters: RTMParameters ): number { const { maxBranchingFactor: K, maxRecallDepth: D } = parameters; // For large N, recall length approaches K^D (soft limit) const softLimit = Math.pow(K, D); // Sublinear growth: C ~ N^(1/D) const sublinearGrowth = Math.pow(narrativeLength, 1 / D); // Combine both effects return Math.min(softLimit, sublinearGrowth * Math.sqrt(K)); } /** * Calculate the variance in recall length across an ensemble * @param narrativeLength - Total number of clauses * @param parameters - RTM parameters * @param ensembleSize - Number of trees in ensemble * @returns Variance in recall length */ export function recallLengthVariance( narrativeLength: number, parameters: RTMParameters, ensembleSize: number ): number { const meanRecall = expectedRecallLength(narrativeLength, parameters); // Variance decreases with ensemble size const baseVariance = meanRecall * 0.2; // 20% coefficient of variation return baseVariance / Math.sqrt(ensembleSize); } /** * Check if a narrative length exhibits scale-invariant properties * @param narrativeLength - Total number of clauses * @param threshold - Minimum length for scale invariance * @returns True if scale-invariant regime */ export function isScaleInvariant( narrativeLength: number, threshold: number = 1000 ): boolean { return narrativeLength >= threshold; } /** * Calculate the scaling exponent for compression distribution * @param compressionRatios - Array of compression ratios * @returns Scaling exponent (slope in log-log plot) */ export function calculateScalingExponent(compressionRatios: number[]): number { if (compressionRatios.length < 2) return 0; // Convert to log scale const logRatios = compressionRatios.map(r => Math.log(r)); const logIndices = compressionRatios.map((_, i) => Math.log(i + 1)); // Linear regression in log-log space const n = logRatios.length; const sumX = logIndices.reduce((a, b) => a + b, 0); const sumY = logRatios.reduce((a, b) => a + b, 0); const sumXY = logIndices.reduce((sum, x, i) => sum + x * logRatios[i]!, 0); const sumX2 = logIndices.reduce((sum, x) => sum + x * x, 0); const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); return slope; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/angrysky56/narrative-graph-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server