pm4js
Version:
Process Mining for Javascript
226 lines (218 loc) • 6.49 kB
JavaScript
class DfgAlignmentsResults {
constructor(logActivities, frequencyDfg, overallResult) {
this.logActivities = logActivities;
this.overallResult = overallResult;
this.frequencyDfg = frequencyDfg;
this.movesUsage = {};
this.totalTraces = this.overallResult.length;
this.fitTraces = 0;
this.totalCost = 0;
this.totalBwc = 0;
this.averageTraceFitness = 0;
for (let alTrace of this.overallResult) {
for (let move of alTrace["alignment"].split(",")) {
if (!(move in this.movesUsage)) {
this.movesUsage[move] = 1;
}
else {
this.movesUsage[move] += 1;
}
}
if (alTrace["cost"] < 1) {
this.fitTraces += 1;
}
this.totalCost += alTrace["cost"];
this.totalBwc += alTrace["bwc"];
this.averageTraceFitness += alTrace["fitness"];
}
this.averageTraceFitness = this.averageTraceFitness / this.overallResult.length;
this.logFitness = 1.0 - (this.totalCost)/(this.totalBwc);
this.percentageFitTraces = this.fitTraces / this.totalTraces;
}
}
class DfgAlignments {
static apply(log, frequencyDfg0, activityKey="concept:name", syncCosts=null, modelMoveCosts=null, logMoveCosts=null) {
let logActivities = GeneralLogStatistics.getAttributeValues(log, activityKey);
let frequencyDfg = frequencyDfg0.getArtificialDfg();
let outgoing = {};
for (let arc0 in frequencyDfg[1]) {
let arc = arc0.split(",");
if (!(arc[0] in outgoing)) {
outgoing[arc[0]] = [];
}
outgoing[arc[0]].push(arc[1]);
}
if (syncCosts == null) {
syncCosts = {};
for (let act in frequencyDfg[0]) {
syncCosts[act] = 0;
}
}
if (modelMoveCosts == null) {
modelMoveCosts = {};
for (let act in frequencyDfg[0]) {
if (act == "■") {
modelMoveCosts[act] = 0;
}
else {
modelMoveCosts[act] = 10000;
}
}
}
if (logMoveCosts == null) {
logMoveCosts = {};
for (let act in logActivities) {
logMoveCosts[act] = 10000;
}
}
let comparator = function(a, b) {
let ret = false;
if (a[0] < b[0]) {
ret = true;
}
else if (a[0] > b[0]) {
ret = false;
}
else {
if (a[1] > b[1]) {
ret = true;
}
else if (a[1] < b[1]) {
ret = false;
}
else {
if (a[2] < b[2]) {
ret = true;
}
else if (a[2] > b[2]) {
ret = false;
}
}
}
return ret;
};
let alignedTraces = {};
let res = [];
let count = 0;
let minPathInModelCost = Math.floor(DfgAlignments.applyTrace([], frequencyDfg, outgoing, syncCosts, modelMoveCosts, logMoveCosts, comparator)["cost"] / 10000);
for (let trace of log.traces) {
let bwc = trace.events.length + minPathInModelCost;
let listAct = [];
for (let eve of trace.events) {
listAct.push(eve.attributes[activityKey].value);
}
if (!(listAct in alignedTraces)) {
let ali = DfgAlignments.applyTrace(listAct, frequencyDfg, outgoing, syncCosts, modelMoveCosts, logMoveCosts, comparator);
let fitness = 1.0;
let dividedCost = Math.floor(ali["cost"] / 10000);
if (bwc > 0) {
fitness = 1.0 - dividedCost / bwc;
}
ali["cost"] = dividedCost;
ali["fitness"] = fitness;
ali["bwc"] = bwc;
alignedTraces[listAct] = ali;
}
res.push(alignedTraces[listAct]);
count++;
}
let ret = new DfgAlignmentsResults(logActivities, frequencyDfg0, res);
Pm4JS.registerObject(ret, "DFG Alignments Result");
return ret;
}
static checkClosed(closedSet, tup) {
if (tup[3] in closedSet) {
if (tup[1] <= closedSet[tup[3]]) {
return true;
}
}
return false;
}
static closeTuple(closedSet, tup) {
closedSet[tup[3]] = tup[1];
}
static applyTrace(listAct, frequencyDfg, outgoing, syncCosts, modelMoveCosts, logMoveCosts, comparator) {
let queue = new PriorityQueue(comparator);
queue.push([0, 0, 0, "▶", false, null, null]);
let count = 0;
let closedSet = {};
while (true) {
count++;
let tup = queue.pop();
if (tup == null) {
return null;
}
else if (tup[3] == "■" && tup[1] == listAct.length) {
return DfgAlignments.formAlignment(listAct, tup);
}
else if (DfgAlignments.checkClosed(closedSet, tup)) {
continue;
}
else {
DfgAlignments.closeTuple(closedSet, tup);
if (tup[3] != "■") {
let enabledTransitions = outgoing[tup[3]];
for (let trans of enabledTransitions) {
let newTup = null;
if (tup[1] < listAct.length && trans == listAct[tup[1]]) {
// sync move
newTup = [tup[0] + syncCosts[trans], tup[1] + 1, count, trans, false, trans, tup];
if (!(DfgAlignments.checkClosed(closedSet, newTup))) {
queue.push(newTup);
}
}
else {
// move on model
newTup = [tup[0] + modelMoveCosts[trans], tup[1], count, trans, true, trans, tup];
if (!(DfgAlignments.checkClosed(closedSet, newTup))) {
queue.push(newTup);
}
}
}
}
if (tup[1] < listAct.length && !(tup[4])) {
// move on log
let newTup = [tup[0] + logMoveCosts[listAct[tup[1]]], tup[1] + 1, count, tup[3], false, null, tup];
if (!(DfgAlignments.checkClosed(closedSet, newTup))) {
queue.push(newTup);
}
}
}
}
return null;
}
static formAlignment(listAct, tup) {
let ret = [];
let cost = tup[0];
let closedStates = tup[2];
tup = tup[6];
while (tup[6] != null) {
let isMM = tup[4];
let currTrans = tup[5];
if (currTrans == null) {
// lm
ret.push("("+listAct[tup[1]-1]+";>>)")
}
else if (isMM) {
ret.push("(>>;"+currTrans+")")
}
else {
ret.push("("+listAct[tup[1]-1]+";"+currTrans+")")
}
tup = tup[6];
}
ret.reverse();
return {"alignment": ret.join(","), "cost": cost, "closedStates": closedStates}
}
}
try {
module.exports = {DfgAlignments: DfgAlignments, DfgAlignmentsResults: DfgAlignmentsResults};
global.DfgAlignments = DfgAlignments;
global.DfgAlignmentsResults = DfgAlignmentsResults;
}
catch (err) {
// not in Node
//console.log(err);
}
Pm4JS.registerAlgorithm("DfgAlignments", "apply", ["EventLog", "FrequencyDfg"], "DfgAlignmentsResults", "Perform Alignments on DFG", "Alessandro Berti");
Pm4JS.registerAlgorithm("DfgAlignments", "apply", ["EventLog", "PerformanceDfg"], "DfgAlignmentsResults", "Perform Alignments on DFG", "Alessandro Berti");