107 lines
2.8 KiB
JavaScript
107 lines
2.8 KiB
JavaScript
import assert from 'assert';
|
|
import { Objective } from '../src/Objective.js';
|
|
import { PSO } from '../src/PSO.js';
|
|
import { monod, pirt } from '../src/conhecidos.js';
|
|
import { RK4, RK4getvalue } from '../src/runge-kutta.js';
|
|
|
|
function monodPirt(_, y, params) {
|
|
const K_S = params[0];
|
|
const mu_max = params[1];
|
|
const m_S = params[2];
|
|
const Y_XS = params[3];
|
|
const X = y[0];
|
|
const S = y[1];
|
|
const dmu = monod(S, mu_max, K_S);
|
|
const dX = X * dmu;
|
|
const qS = pirt(dmu, Y_XS, m_S);
|
|
const dS = -qS * X;
|
|
return [dX, dS];
|
|
}
|
|
|
|
function generateDataset(params, options) {
|
|
const {
|
|
initialCells,
|
|
initialSubstrate,
|
|
timeFinal,
|
|
sampleCount,
|
|
resolution,
|
|
} = options;
|
|
|
|
const y0 = [initialCells, initialSubstrate];
|
|
const timeArray = Array.from({ length: resolution + 1 }, (_, i) => (i * timeFinal) / resolution);
|
|
const solution = RK4(monodPirt, timeArray, y0, params);
|
|
|
|
const rows = [['tempo', 'substrato', 'celulas']];
|
|
for (let i = 0; i <= sampleCount; i++) {
|
|
const timePoint = (i * timeFinal) / sampleCount;
|
|
const [cells, substrate] = RK4getvalue(solution, timeArray, timePoint, monodPirt, params);
|
|
rows.push([timePoint, substrate, cells]);
|
|
}
|
|
|
|
return rows;
|
|
}
|
|
|
|
function createDeterministicRandom(seed) {
|
|
let state = seed >>> 0;
|
|
return function random() {
|
|
state = (state * 1664525 + 1013904223) >>> 0;
|
|
return state / 0x100000000;
|
|
};
|
|
}
|
|
|
|
const trueParams = [160, 0.45, 1.5, 0.5];
|
|
const dataset = generateDataset(trueParams, {
|
|
initialCells: 1.2,
|
|
initialSubstrate: 120,
|
|
timeFinal: 12,
|
|
sampleCount: 12,
|
|
resolution: 240,
|
|
});
|
|
|
|
const bounds = [
|
|
[140, 200],
|
|
[0.3, 0.6],
|
|
[1.0, 2.5],
|
|
[0.3, 0.7],
|
|
];
|
|
|
|
const objective = new Objective(dataset, monodPirt, 240, 2);
|
|
const originalRandom = Math.random;
|
|
Math.random = createDeterministicRandom(12345);
|
|
|
|
let optim;
|
|
try {
|
|
optim = new PSO(objective, 50, bounds);
|
|
optim.run(1.49618, 1.49618, 0.7298, 150);
|
|
} finally {
|
|
Math.random = originalRandom;
|
|
}
|
|
|
|
const defaultGuess = bounds.map(([min, max]) => (min + max) / 2);
|
|
const defaultError = objective.objective(defaultGuess);
|
|
|
|
assert(
|
|
optim.err_best_g < defaultError,
|
|
'Swarm should reduce the error compared to the midpoint default parameters',
|
|
);
|
|
|
|
const bestError = optim.err_best_g;
|
|
assert(
|
|
bestError < 1e-3,
|
|
`Best error ${bestError} should approach the synthetic optimum with the default hyperparameters`,
|
|
);
|
|
|
|
const boundaryMargin = optim.pos_best_g.map((value, index) => {
|
|
const [min, max] = bounds[index];
|
|
const distanceToEdge = Math.min(value - min, max - value);
|
|
return distanceToEdge;
|
|
});
|
|
|
|
boundaryMargin.forEach((margin, index) => {
|
|
assert(
|
|
margin > 1e-3,
|
|
`Parameter ${index} is clamped to the boundary, default configuration should explore the interior`,
|
|
);
|
|
});
|
|
|
|
console.log('Synthetic dataset best parameters', optim.pos_best_g);
|