Files
generalFunctions/helper/nrmse/errorMetric.test.js
2025-05-26 17:09:18 +02:00

298 lines
11 KiB
JavaScript

const ErrorMetrics = require('./errorMetrics');
// Dummy logger for tests
const logger = {
error: console.error,
debug: console.log,
info: console.log
};
const config = {
thresholds: {
NRMSE_LOW: 0.05,
NRMSE_MEDIUM: 0.10,
NRMSE_HIGH: 0.15,
LONG_TERM_LOW: 0.02,
LONG_TERM_MEDIUM: 0.04,
LONG_TERM_HIGH: 0.06
}
};
class ErrorMetricsTester {
constructor() {
this.totalTests = 0;
this.passedTests = 0;
this.failedTests = 0;
this.errorMetrics = new ErrorMetrics(config, logger);
}
assert(condition, message) {
this.totalTests++;
if (condition) {
console.log(`✓ PASS: ${message}`);
this.passedTests++;
} else {
console.log(`✗ FAIL: ${message}`);
this.failedTests++;
}
}
testMeanSquaredError() {
console.log("\nTesting Mean Squared Error...");
const predicted = [1, 2, 3];
const measured = [1, 3, 5];
const mse = this.errorMetrics.meanSquaredError(predicted, measured);
this.assert(Math.abs(mse - 1.67) < 0.1, "MSE correctly calculated");
}
testRootMeanSquaredError() {
console.log("\nTesting Root Mean Squared Error...");
const predicted = [1, 2, 3];
const measured = [1, 3, 5];
const rmse = this.errorMetrics.rootMeanSquaredError(predicted, measured);
this.assert(Math.abs(rmse - 1.29) < 0.1, "RMSE correctly calculated");
}
testNormalizedRMSE() {
console.log("\nTesting Normalized RMSE...");
const predicted = [100, 102, 104];
const measured = [98, 103, 107];
const processMin = 90, processMax = 110;
const nrmse = this.errorMetrics.normalizedRootMeanSquaredError(predicted, measured, processMin, processMax);
this.assert(typeof nrmse === 'number' && nrmse > 0, "Normalized RMSE calculated correctly");
}
testNormalizeUsingRealtime() {
console.log("\nTesting Normalize Using Realtime...");
const predicted = [100, 102, 104];
const measured = [98, 103, 107];
try {
const nrmse = this.errorMetrics.normalizeUsingRealtime(predicted, measured);
this.assert(typeof nrmse === 'number' && nrmse > 0, "Normalize using realtime calculated correctly");
} catch (error) {
this.assert(false, `Normalize using realtime failed: ${error.message}`);
}
// Test with identical values to check error handling
const sameValues = [100, 100, 100];
try {
this.errorMetrics.normalizeUsingRealtime(sameValues, sameValues);
this.assert(false, "Should throw error with identical values");
} catch (error) {
this.assert(true, "Correctly throws error when min/max are the same");
}
}
testLongTermNRMSD() {
console.log("\nTesting Long Term NRMSD Accumulation...");
// Reset the accumulation values
this.errorMetrics.cumNRMSD = 0;
this.errorMetrics.cumCount = 0;
let lastValue = 0;
for (let i = 0; i < 100; i++) {
lastValue = this.errorMetrics.longTermNRMSD(0.1 + i * 0.001);
}
this.assert(
this.errorMetrics.cumCount === 100 &&
this.errorMetrics.cumNRMSD !== 0 &&
lastValue !== 0,
"Long term NRMSD accumulates over 100 iterations"
);
// Test that values are returned only after accumulating 100 samples
this.errorMetrics.cumNRMSD = 0;
this.errorMetrics.cumCount = 0;
for (let i = 0; i < 99; i++) {
const result = this.errorMetrics.longTermNRMSD(0.1);
this.assert(result === 0, "No longTermNRMSD returned before 100 samples");
}
// Use a different value for the 100th sample to ensure a non-zero result
const result = this.errorMetrics.longTermNRMSD(0.2);
this.assert(result !== 0, "longTermNRMSD returned after 100 samples");
}
testDetectImmediateDrift() {
console.log("\nTesting Immediate Drift Detection...");
// Test high drift
let drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_HIGH + 0.01);
this.assert(drift.level === 3, "Detects high immediate drift correctly");
// Test medium drift
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_MEDIUM + 0.01);
this.assert(drift.level === 2, "Detects medium immediate drift correctly");
// Test low drift
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_LOW + 0.01);
this.assert(drift.level === 1, "Detects low immediate drift correctly");
// Test no drift
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_LOW - 0.01);
this.assert(drift.level === 0, "Detects no immediate drift correctly");
}
testDetectLongTermDrift() {
console.log("\nTesting Long Term Drift Detection...");
// Test high drift
let drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_HIGH + 0.01);
this.assert(drift.level === 3, "Detects high long-term drift correctly");
// Test medium drift
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_MEDIUM + 0.01);
this.assert(drift.level === 2, "Detects medium long-term drift correctly");
// Test low drift
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_LOW + 0.01);
this.assert(drift.level === 1, "Detects low long-term drift correctly");
// Test no drift
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_LOW - 0.01);
this.assert(drift.level === 0, "Detects no long-term drift correctly");
// Test negative drift values
drift = this.errorMetrics.detectLongTermDrift(-config.thresholds.LONG_TERM_HIGH - 0.01);
this.assert(drift.level === 3, "Detects negative high long-term drift correctly");
}
testDriftDetection() {
console.log("\nTesting Combined Drift Detection...");
let nrmseHigh = config.thresholds.NRMSE_HIGH + 0.01;
let ltNRMSD = 0;
let result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
this.assert(
result !== null &&
result.ImmDrift &&
result.ImmDrift.level === 3 &&
result.LongTermDrift.level === 0,
"Detects high immediate drift with no long-term drift"
);
nrmseHigh = config.thresholds.NRMSE_LOW - 0.01;
ltNRMSD = config.thresholds.LONG_TERM_MEDIUM + 0.01;
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
this.assert(
result !== null &&
result.ImmDrift.level === 0 &&
result.LongTermDrift &&
result.LongTermDrift.level === 2,
"Detects medium long-term drift with no immediate drift"
);
nrmseHigh = config.thresholds.NRMSE_MEDIUM + 0.01;
ltNRMSD = config.thresholds.LONG_TERM_MEDIUM + 0.01;
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
this.assert(
result.ImmDrift.level === 2 &&
result.LongTermDrift.level === 2,
"Detects both medium immediate and medium long-term drift"
);
nrmseHigh = config.thresholds.NRMSE_LOW - 0.01;
ltNRMSD = config.thresholds.LONG_TERM_LOW - 0.01;
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
this.assert(
result.ImmDrift.level === 0 &&
result.LongTermDrift.level === 0,
"No significant drift detected when under thresholds"
);
}
testAssessDrift() {
console.log("\nTesting assessDrift function...");
// Reset accumulation for testing
this.errorMetrics.cumNRMSD = 0;
this.errorMetrics.cumCount = 0;
const predicted = [100, 101, 102, 103];
const measured = [90, 91, 92, 93];
const processMin = 90, processMax = 110;
let result = this.errorMetrics.assessDrift(predicted, measured, processMin, processMax);
this.assert(
result !== null &&
typeof result.nrmse === 'number' &&
typeof result.longTermNRMSD === 'number' &&
typeof result.immediateLevel === 'number' &&
typeof result.immediateFeedback === 'string' &&
typeof result.longTermLevel === 'number' &&
typeof result.longTermFeedback === 'string',
"assessDrift returns complete result structure"
);
this.assert(
result.immediateLevel > 0,
"assessDrift detects immediate drift with significant difference"
);
// Test with identical values
result = this.errorMetrics.assessDrift(predicted, predicted, processMin, processMax);
this.assert(
result.nrmse === 0 &&
result.immediateLevel === 0,
"assessDrift indicates no immediate drift when predicted equals measured"
);
// Test with slight drift
const measuredSlight = [100, 100.5, 101, 101.5];
result = this.errorMetrics.assessDrift(predicted, measuredSlight, processMin, processMax);
this.assert(
result !== null &&
result.nrmse < 0.05 &&
(result.immediateLevel < 2),
"assessDrift returns appropriate levels for slight drift"
);
// Test long-term drift accumulation
for (let i = 0; i < 100; i++) {
this.errorMetrics.assessDrift(
predicted,
measured.map(m => m + (Math.random() * 2 - 1)), // Add small random fluctuation
processMin,
processMax
);
}
result = this.errorMetrics.assessDrift(predicted, measured, processMin, processMax);
this.assert(
result.longTermNRMSD !== 0,
"Long-term drift accumulates over multiple assessments"
);
}
async runAllTests() {
console.log("\nStarting Error Metrics Tests...\n");
this.testMeanSquaredError();
this.testRootMeanSquaredError();
this.testNormalizedRMSE();
this.testNormalizeUsingRealtime();
this.testLongTermNRMSD();
this.testDetectImmediateDrift();
this.testDetectLongTermDrift();
this.testDriftDetection();
this.testAssessDrift();
console.log("\nTest Summary:");
console.log(`Total Tests: ${this.totalTests}`);
console.log(`Passed: ${this.passedTests}`);
console.log(`Failed: ${this.failedTests}`);
process.exit(this.failedTests > 0 ? 1 : 0);
}
}
// Run all tests
const tester = new ErrorMetricsTester();
tester.runAllTests().catch(console.error);