diff --git a/source/engine/traverse.js b/source/engine/traverse.js
index 7a3e24693..c01b1ffce 100644
--- a/source/engine/traverse.js
+++ b/source/engine/traverse.js
@@ -36,6 +36,8 @@ import {
applyOrEmpty
} from './traverse-common-functions'
+import uniroot from './uniroot'
+
let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
/*
@@ -403,34 +405,13 @@ export let computeRuleValue = (formuleValue, isApplicable) =>
? formuleValue
: isApplicable === false ? 0 : formuleValue == 0 ? 0 : null
-let computeInversion = (objective, computeGivenInput, currentValue) => {
- let v = currentValue || objective, // notre première approximation est l'objectif lui-même (on suppose donc qu'ils sont du même ordre de grandeur, ce qui est vrai pour les salaires mais pas forcément pour d'autres variables évidemment)
- here = computeGivenInput(v)
-
- console.log('coucou', v, here)
-
- if (Math.abs(here - objective) < 20 ) {
- return v
- }
-
- let
- ascend = computeGivenInput(v + 10),
- descend = computeGivenInput(v - 10)
-
- if (Math.abs(ascend - objective) < Math.abs(descend - objective))
- return computeInversion(objective, computeGivenInput, v + 10)
- else
- return computeInversion(objective, computeGivenInput, v - 10)
-
-}
-
export let treatRuleRoot = (rules, rule) => {
let evaluate = (situationGate, parsedRules, r) => {
let inversions = r['inversions possibles']
if (inversions) {
/*
- Quel inversion possible est renseignée dans la situation courante ?
+ Quelle inversion possible est renseignée dans la situation courante ?
Ex. s'il nous est demandé de calculer le salaire de base, est-ce qu'un candidat à l'inversion, comme
le salaire net, a été renseigné ?
*/
@@ -439,14 +420,18 @@ export let treatRuleRoot = (rules, rule) => {
if (fixedObjective != null) {
let
fixedObjectiveRule = findRuleByName(parsedRules, fixedObjective),
- nodeValue = computeInversion(
- situationGate(fixedObjective),
- i =>
- evaluateNode(
- n => (r.name === n || n === 'sys.filter') ? i : situationGate(n),
- parsedRules,
- fixedObjectiveRule
- ).nodeValue
+ fx = x => evaluateNode(
+ n => (r.name === n || n === 'sys.filter') ? x : situationGate(n), //TODO pourquoi doit-on nous préoccuper de sys.filter ?
+ parsedRules,
+ fixedObjectiveRule
+ ).nodeValue,
+ tolerancePercentage = 0.00001,
+ nodeValue = uniroot(
+ x => fx(x) - situationGate(fixedObjective),
+ 0,
+ 1000000000,
+ tolerancePercentage * situationGate(fixedObjective),
+ 100
)
return {nodeValue}
diff --git a/source/engine/uniroot.js b/source/engine/uniroot.js
new file mode 100644
index 000000000..2fe6e3197
--- /dev/null
+++ b/source/engine/uniroot.js
@@ -0,0 +1,116 @@
+/**
+ * Searches the interval from lowerLimit to upperLimit
+ * for a root (i.e., zero) of the function func with respect to
+ * its first argument using Brent's method root-finding algorithm.
+ *
+ * Translated from zeroin.c in http://www.netlib.org/c/brent.shar.
+ *
+ * Copyright (c) 2012 Borgar Thorsteinsson
+ * MIT License, http://www.opensource.org/licenses/mit-license.php
+ *
+ * @param {function} function for which the root is sought.
+ * @param {number} the lower point of the interval to be searched.
+ * @param {number} the upper point of the interval to be searched.
+ * @param {number} the desired accuracy (convergence tolerance).
+ * @param {number} the maximum number of iterations.
+ * @returns an estimate for the root within accuracy.
+ *
+ */
+export default function uniroot(func, lowerLimit, upperLimit, errorTol, maxIter) {
+ var a = lowerLimit,
+ b = upperLimit,
+ c = a,
+ fa = func(a),
+ fb = func(b),
+ fc = fa,
+ tol_act, // Actual tolerance
+ new_step, // Step at this iteration
+ prev_step, // Distance from the last but one to the last approximation
+ p, // Interpolation step is calculated in the form p/q; division is delayed until the last moment
+ q
+
+ errorTol = errorTol || 0
+ maxIter = maxIter || 1000
+
+ while (maxIter-- > 0) {
+ prev_step = b - a
+
+ if (Math.abs(fc) < Math.abs(fb)) {
+ // Swap data for b to be the best approximation
+ (a = b), (b = c), (c = a)
+ ;(fa = fb), (fb = fc), (fc = fa)
+ }
+
+ tol_act = 1e-15 * Math.abs(b) + errorTol / 2
+ new_step = (c - b) / 2
+
+ if (Math.abs(new_step) <= tol_act || fb === 0) {
+ return b // Acceptable approx. is found
+ }
+
+ // Decide if the interpolation can be tried
+ if (Math.abs(prev_step) >= tol_act && Math.abs(fa) > Math.abs(fb)) {
+ // If prev_step was large enough and was in true direction, Interpolatiom may be tried
+ var t1, cb, t2
+ cb = c - b
+ if (a === c) {
+ // If we have only two distinct points linear interpolation can only be applied
+ t1 = fb / fa
+ p = cb * t1
+ q = 1.0 - t1
+ } else {
+ // Quadric inverse interpolation
+ (q = fa / fc), (t1 = fb / fc), (t2 = fb / fa)
+ p = t2 * (cb * q * (q - t1) - (b - a) * (t1 - 1))
+ q = (q - 1) * (t1 - 1) * (t2 - 1)
+ }
+
+ if (p > 0) {
+ q = -q // p was calculated with the opposite sign; make p positive
+ } else {
+ p = -p // and assign possible minus to q
+ }
+
+ if (
+ p < 0.75 * cb * q - Math.abs(tol_act * q) / 2 &&
+ p < Math.abs(prev_step * q / 2)
+ ) {
+ // If (b + p / q) falls in [b,c] and isn't too large it is accepted
+ new_step = p / q
+ }
+
+ // If p/q is too large then the bissection procedure can reduce [b,c] range to more extent
+ }
+
+ if (Math.abs(new_step) < tol_act) {
+ // Adjust the step to be not less than tolerance
+ new_step = new_step > 0 ? tol_act : -tol_act
+ }
+
+ (a = b), (fa = fb) // Save the previous approx.
+ ;(b += new_step), (fb = func(b)) // Do step to a new approxim.
+
+ if ((fb > 0 && fc > 0) || (fb < 0 && fc < 0)) {
+ (c = a), (fc = fa) // Adjust c for it to have a sign opposite to that of b
+ }
+ }
+}
+
+/*
+var test_counter;
+function f1 (x) { test_counter++; return (Math.pow(x,2)-1)*x - 5; }
+function f2 (x) { test_counter++; return Math.cos(x)-x; }
+function f3 (x) { test_counter++; return Math.sin(x)-x; }
+function f4 (x) { test_counter++; return (x + 3) * Math.pow(x - 1, 2); }
+[
+ [f1, 2, 3],
+ [f2, 2, 3],
+ [f2, -1, 3],
+ [f3, -1, 3],
+ [f4, -4, 4/3]
+].forEach(function (args) {
+ test_counter = 0;
+ var root = uniroot.apply( pv, args );
+ ;;;console.log( 'uniroot:', args.slice(1), root, test_counter );
+})
+*/
diff --git a/test/inversion.test.js b/test/inversion.test.js
index 78fb46399..a428f893b 100644
--- a/test/inversion.test.js
+++ b/test/inversion.test.js
@@ -42,12 +42,12 @@ describe("inversions", () => {
- nom: brut
format: euro
- inversions possibles:
+ inversions possibles:
- net
`,
rules = yaml.safeLoad(rawRules).map(enrichRule),
analysis = analyseSituation(rules, "brut")(stateSelector)
- expect(analysis.nodeValue).to.be.closeTo(2570, 0.001)
+ expect(analysis.nodeValue).to.be.closeTo(2000/(77/100), 0.0001*2000)
})
})