← Source tree

web_demo/inference.js

/* Original BSD-3-Clause evaluator for this project's synthetic trained forest. */
(function (root, factory) {
  const api = factory();
  if (typeof module === "object" && module.exports) module.exports = api;
  else root.ReviewInference = api;
})(globalThis, function () {
  "use strict";
  function decimal(text) {
    if (typeof text !== "string" || !/^[+-]?\d+(?:\.\d+)?$/.test(text.trim()))
      throw new Error("Use plain decimal numbers, without commas or exponents.");
    const value = Number(text);
    if (!Number.isFinite(value) || Math.abs(value) > 1e15)
      throw new Error("Number is outside the supported range.");
    return value;
  }
  function score(model, amountText, quantityText) {
    const amount = decimal(amountText), quantity = decimal(quantityText);
    if (!(quantity > 0)) throw new Error("Quantity must be positive.");
    const signedLog = n => Math.sign(n) * Math.log1p(Math.abs(n));
    const original = [signedLog(amount), Math.log1p(quantity), signedLog(amount / quantity)];
    if (original.some(v => !Number.isFinite(v))) throw new Error("Derived value is outside the supported range.");
    // sklearn's tree traversal accepts float32 features; bounds use unrounded features.
    const x = original.map(Math.fround);
    let total = 0;
    for (const tree of model.trees) {
      let node = 0;
      while (tree.left[node] !== -1) {
        node = x[tree.feature[node]] <= tree.threshold[node] ? tree.left[node] : tree.right[node];
      }
      total += tree.leaf_depth[node];
    }
    const decisionScore = .5 - 2 ** (-total / (model.trees.length * model.normalizer));
    const outside = original.some((v, i) => v < model.bounds[i][0] || v > model.bounds[i][1]);
    return { decision_score: decisionScore, model_candidate: decisionScore < 0,
      outside_reference_support: outside,
      needs_review: decisionScore < 0 && outside,
      notice: "Synthetic USD reference only. Score is not an error probability." };
  }
  return { score };
});