Corrige le calcul des variables manquantes des variations
parent
768e7aee6b
commit
03d1228324
|
@ -5,6 +5,7 @@ import {
|
|||
equals,
|
||||
reduce,
|
||||
chain,
|
||||
length,
|
||||
fromPairs,
|
||||
keys,
|
||||
values,
|
||||
|
|
|
@ -41,6 +41,7 @@ import { findRuleByDottedName, disambiguateRuleReference } from './rules'
|
|||
|
||||
export let collectMissingVariables = targets => {
|
||||
let missing = flatten(pluck('missingVariables', targets))
|
||||
console.log("total # missing", length(missing))
|
||||
return groupBy(identity, missing)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import {
|
||||
flatten,
|
||||
length,
|
||||
objOf,
|
||||
toPairs,
|
||||
dissoc,
|
||||
|
@ -144,7 +146,8 @@ let devariate = (recurse, k, v) => {
|
|||
}
|
||||
|
||||
let explanation = map(evaluateOne, node.explanation),
|
||||
choice = find(node => node.condition.nodeValue, explanation),
|
||||
satisfied = filter(node => node.condition.nodeValue, explanation),
|
||||
choice = head(satisfied),
|
||||
nodeValue = choice ? choice.nodeValue : null
|
||||
|
||||
let leftMissing = choice
|
||||
|
@ -152,9 +155,9 @@ let devariate = (recurse, k, v) => {
|
|||
: uniq(
|
||||
map(collectNodeMissing, pluck('condition', explanation))
|
||||
),
|
||||
rightMissing = choice
|
||||
? choice.missingVariables
|
||||
: map(collectNodeMissing, explanation),
|
||||
rightMissing = !choice
|
||||
? []
|
||||
: map(collectNodeMissing, satisfied),
|
||||
missingVariables = concat(leftMissing, rightMissing || [])
|
||||
|
||||
return rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
|
@ -423,7 +426,7 @@ let doInversion = (situationGate, parsedRules, v, dottedName) => {
|
|||
|
||||
if (inversion.inversionChoiceNeeded)
|
||||
return {
|
||||
inversionMissingVariables: [dottedName],
|
||||
missingVariables: [dottedName],
|
||||
nodeValue: null
|
||||
}
|
||||
let { fixedObjectiveValue, fixedObjectiveRule } = inversion
|
||||
|
@ -435,28 +438,21 @@ let doInversion = (situationGate, parsedRules, v, dottedName) => {
|
|||
n => (dottedName === n ? x : situationGate(n)),
|
||||
parsedRules,
|
||||
fixedObjectiveRule
|
||||
).nodeValue
|
||||
)
|
||||
}
|
||||
|
||||
// si fx renvoie null pour une valeur numérique standard, disons 1000, on peut
|
||||
// considérer que l'inversion est impossible du fait de variables manquantes
|
||||
// TODO fx peut être null pour certains x, et valide pour d'autres : on peut implémenter ici le court-circuit
|
||||
if (fx(1000) == null)
|
||||
return {
|
||||
nodeValue: null,
|
||||
inversionMissingVariables:
|
||||
evaluateNode(
|
||||
{},
|
||||
n => (dottedName === n ? 1000 : situationGate(n)),
|
||||
parsedRules,
|
||||
fixedObjectiveRule
|
||||
).missingVariables
|
||||
}
|
||||
let attempt = fx(1000)
|
||||
if (attempt.nodeValue == null) {
|
||||
return attempt
|
||||
}
|
||||
|
||||
let tolerancePercentage = 0.00001,
|
||||
// cette fonction détermine la racine d'une fonction sans faire trop d'itérations
|
||||
nodeValue = uniroot(
|
||||
x => fx(x) - fixedObjectiveValue,
|
||||
x => fx(x).nodeValue - fixedObjectiveValue,
|
||||
0,
|
||||
1000000000,
|
||||
tolerancePercentage * fixedObjectiveValue,
|
||||
|
@ -465,7 +461,7 @@ let doInversion = (situationGate, parsedRules, v, dottedName) => {
|
|||
|
||||
return {
|
||||
nodeValue,
|
||||
inversionMissingVariables: [],
|
||||
missingVariables: [],
|
||||
inversionCache
|
||||
}
|
||||
}
|
||||
|
@ -477,12 +473,12 @@ export let mecanismInversion = dottedName => (recurse, k, v) => {
|
|||
situationGate(dottedName) == undefined &&
|
||||
doInversion(situationGate, parsedRules, v, dottedName),
|
||||
nodeValue = inversion.nodeValue,
|
||||
missingVariables = inversion.inversionMissingVariables
|
||||
missingVariables = inversion.missingVariables
|
||||
|
||||
let evaluatedNode = rewriteNode(node, nodeValue, null, missingVariables)
|
||||
|
||||
// rewrite the simulation cache with the definitive inversion values
|
||||
toPairs(inversion.inversionCache).map(([k, v]) => (cache[k] = v))
|
||||
// toPairs(inversion.inversionCache).map(([k, v]) => (cache[k] = v))
|
||||
return evaluatedNode
|
||||
}
|
||||
|
||||
|
|
|
@ -129,38 +129,35 @@ let fillVariableNode = (rules, rule, filter) => parseResult => {
|
|||
// On va vérifier dans le cache courant, dict, si la variable n'a pas été déjà évaluée
|
||||
// En effet, l'évaluation dans le cas d'une variable qui a une formule, est coûteuse !
|
||||
cacheName = dottedName + (filter ? '.' + filter : ''),
|
||||
cached = cache[cacheName],
|
||||
// make parsedRules a dict object, that also serves as a cache of evaluation ?
|
||||
variable = cached
|
||||
? cached
|
||||
: findRuleByDottedName(parsedRules, dottedName),
|
||||
cached = cache[cacheName]
|
||||
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
let variable = findRuleByDottedName(parsedRules, dottedName),
|
||||
variableIsCalculable = variable.formule != null,
|
||||
parsedRule =
|
||||
variableIsCalculable &&
|
||||
(cached
|
||||
? cached
|
||||
: evaluateNode(cache, situation, parsedRules, variable)),
|
||||
// evaluateVariable renvoit la valeur déduite de la situation courante renseignée par l'utilisateur
|
||||
situationValue = evaluateVariable(situation, dottedName, variable),
|
||||
needsEvaluation = (variableIsCalculable && situationValue == null),
|
||||
parsedRule = needsEvaluation
|
||||
? evaluateNode(cache, situation, parsedRules, variable)
|
||||
: variable,
|
||||
// evaluateVariable renvoit la valeur déduite de la situation courante renseignée par l'utilisateur
|
||||
explanation = parsedRule,
|
||||
nodeValue =
|
||||
situationValue != null
|
||||
? situationValue // cette variable a été directement renseignée
|
||||
: variableIsCalculable
|
||||
? parsedRule.nodeValue // la valeur du calcul fait foi
|
||||
: null, // elle restera donc nulle
|
||||
explanation = parsedRule,
|
||||
missingVariables = nodeValue != null // notamment si situationValue != null
|
||||
? []
|
||||
: variableIsCalculable
|
||||
? parsedRule.missingVariables
|
||||
: [dottedName]
|
||||
|
||||
if (cached) {
|
||||
return cached
|
||||
} else {
|
||||
cache[cacheName] = rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
return cache[cacheName]
|
||||
}
|
||||
cache[cacheName] = rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
return cache[cacheName]
|
||||
}
|
||||
|
||||
let { fragments } = parseResult,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { length } from 'ramda'
|
||||
import { expect } from 'chai'
|
||||
import { rules as realRules, enrichRule } from '../source/engine/rules'
|
||||
import { analyse, analyseMany, parseAll } from '../source/engine/traverse'
|
||||
|
@ -105,8 +106,23 @@ describe('inversions', () => {
|
|||
- net
|
||||
- nom: cadre
|
||||
- nom: assiette
|
||||
formule: 67 + brut
|
||||
|
||||
formule:
|
||||
somme:
|
||||
- 1200
|
||||
- brut
|
||||
- taxeOne
|
||||
- nom: taxeOne
|
||||
non applicable si: cadre
|
||||
formule: taxe + taxe
|
||||
- nom: taxe
|
||||
formule:
|
||||
multiplication:
|
||||
assiette: 1200
|
||||
variations:
|
||||
- si: cadre
|
||||
taux: 80%
|
||||
- si: ≠ cadre
|
||||
taux: 70%
|
||||
`,
|
||||
rules = parseAll(yaml.safeLoad(rawRules).map(enrichRule)),
|
||||
stateSelector = name => ({ net: 2000 }[name]),
|
||||
|
|
Loading…
Reference in New Issue