consys
Version:
consys is a flexible tool to evaluate models using generic and readable constraints.
319 lines (318 loc) • 11.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Util_1 = __importStar(require("./Util"));
const Constraint_1 = __importDefault(require("./Constraint"));
const TextProcessor_1 = __importDefault(require("./dsl/TextProcessor"));
/**
* A constraint system with multiple constraints and custom functions, defined for specific model and state types.
*/
class ConstraintSystem {
constructor() {
this.constraints = [];
this.functions = {};
this.evaluationData = null;
this.updatedFunctions = true;
}
/**
* For a given evaluation filter type, return the corresponding function
*
* @param evaluationFilter filter type
* @private
*/
static getFilterFunction(evaluationFilter) {
switch (evaluationFilter) {
case 'all':
return () => true;
case 'consistent':
return (evaluation) => evaluation.consistent;
case 'inconsistent':
return (evaluation) => !evaluation.consistent;
default:
return (evaluation) => evaluationFilter(evaluation.resource);
}
}
/**
* Registers a plugin for this constraint system.
*
* @param plugin plugin to be registered
*/
registerPlugin(plugin) {
return __awaiter(this, void 0, void 0, function* () {
yield plugin.registerFunctions(this);
yield plugin.registerConstraints(this);
});
}
/**
* Adds a constraint to this constraint system.
*
* @param resource constraint data
*/
addConstraint(resource) {
this.constraints.push(new Constraint_1.default(resource));
}
/**
* Adds an array of constraints to this constraint system.
*
* @param resources constraint data
*/
addConstraints(resources) {
for (let resource of resources) {
this.addConstraint(resource);
}
}
/**
* Adds a custom function to this constraint system.
*
* @param name name of the function
* @param fun custom function
*/
addFunction(name, fun) {
this.addFun(name, fun);
}
/**
* Adds a custom statement (function without any arguments) to this constraint system.
*
* @param name name of the statement
* @param fun custom statement
*/
addStatement(name, fun) {
this.addFun(name, fun);
}
/**
* Evaluates one or more models and a state given all registered constraints and functions.
*
* @param model model to be evaluated
* @param state state to be evaluated
* @param include optionally filter evaluations of reports
*/
evaluate(model, state, include = 'all') {
let reports;
if (model instanceof Array) {
reports = this.evaluateMultiple(model, state);
}
else {
reports = [this.evaluateSingle(model, state)];
}
this.filterReportEvaluation(reports, include);
this.updatedFunctions = false;
return reports;
}
/**
* Helper function to add a custom function or statement.
*
* @param name name of the function or statement
* @param fun custom function or statement
* @private
*/
addFun(name, fun) {
if (this.functions[name] !== undefined) {
throw Util_1.Log.error('Function with name ' + name + ' is already registered');
}
this.functions[name] = fun;
this.updatedFunctions = true;
}
/**
* Filters evaluations of reports based on a given function.
*
* @param reports reports for which evaluations are filtered
* @param evaluationFilter filter function
* @private
*/
filterReportEvaluation(reports, evaluationFilter) {
for (let report of reports) {
let evaluation = report.evaluation;
report.evaluation = evaluation.filter(ConstraintSystem.getFilterFunction(evaluationFilter));
}
}
/**
* Evaluates multiple models and a single state.
*
* @param models models to be evaluated
* @param state state to be evaluated
* @private
*/
evaluateMultiple(models, state) {
let reports = [];
for (let model of models) {
let report = this.evaluateSingle(model, state);
reports.push(report);
}
return reports;
}
/**
* Evaluates a single model and state.
*
* @param model model to be evaluated
* @param state state to be evaluated
* @private
*/
evaluateSingle(model, state) {
let evaluationRes = [];
this.updateEvaluationData(model, state);
for (let constraint of this.constraints) {
let evaluation = constraint.evaluate(this.evaluationData, this.updatedFunctions);
evaluationRes.push(evaluation);
}
let constraintData = this.getConstraintData();
return {
checkedModel: model,
checkedState: state,
checkedConstraints: constraintData,
evaluation: evaluationRes,
};
}
/**
* Accumulates the occurrences of keys in src to the keys in res.
*
* @param src src counts
* @param res res counts
* @private
*/
static accumulateOccurrences(src, res) {
for (let srcKey of Object.keys(src)) {
res[srcKey] += src[srcKey];
}
}
/**
* Returns statistical data about the occurrences of model and state variables in consistent or inconsistent
* constraints. This can be used to determine which variable has the most influence on the result.
*
* @param model model to be evaluated
* @param state state to be evaluated
*/
evaluateStatistics(model, state) {
let res = {
totalConstraints: this.constraints.length,
consistent: {
total: 0,
model: Util_1.default.initCounts(model, 0),
state: Util_1.default.initCounts(state, 0),
},
inconsistent: {
total: 0,
model: Util_1.default.initCounts(model, 0),
state: Util_1.default.initCounts(state, 0),
},
};
this.updateEvaluationData(model, state);
for (let constraint of this.constraints) {
if (constraint.isConsistent(this.evaluationData)) {
res.consistent.total++;
ConstraintSystem.accumulateOccurrences(constraint.getModelVarOccurrences(), res.consistent.model);
ConstraintSystem.accumulateOccurrences(constraint.getStateVarOccurrences(), res.consistent.state);
}
else {
res.inconsistent.total++;
ConstraintSystem.accumulateOccurrences(constraint.getModelVarOccurrences(), res.inconsistent.model);
ConstraintSystem.accumulateOccurrences(constraint.getStateVarOccurrences(), res.inconsistent.state);
}
}
return res;
}
/**
* Returns the number of constraints that are inconsistent with the given model and state.
*
* @param model model to be evaluated
* @param state state to be evaluated
*/
getNumInconsistentConstraints(model, state) {
this.updateEvaluationData(model, state);
let numInconsistent = 0;
for (let constraint of this.constraints) {
if (!constraint.isConsistent(this.evaluationData)) {
numInconsistent++;
}
}
return numInconsistent;
}
/**
* Returns the number of constraints that are consistent with the given model and state.
*
* @param model model to be evaluated
* @param state state to be evaluated
*/
getNumConsistentConstraints(model, state) {
return (this.constraints.length - this.getNumInconsistentConstraints(model, state));
}
/**
* Returns the number of constraints that were added to this system.
*/
getNumConstraints() {
return this.constraints.length;
}
/**
* Updates the data used to evaluate constraints with the new model, state and functions.
*
* @param model new model
* @param state new state
* @private
*/
updateEvaluationData(model, state) {
if (!this.evaluationData) {
this.evaluationData = {
model: model,
state: state,
functions: this.functions,
};
}
else {
this.evaluationData.model = model;
this.evaluationData.state = state;
this.evaluationData.functions = this.functions;
}
}
/**
* Returns a converted message string by replacing function calls or model and state variables with their
* actual current value.
*
* @param msgString message to be converted
* @param model model
* @param state state
*/
getMessage(msgString, model, state) {
return new TextProcessor_1.default(msgString).process(model, state, this.functions);
}
/**
* Returns all constraint data that was registered.
* @private
*/
getConstraintData() {
let res = [];
for (let constraint of this.constraints) {
res.push(constraint.getResource());
}
return res;
}
}
exports.default = ConstraintSystem;