ai-planning-val
Version:
Javascript/typescript wrapper for VAL (AI Planning plan validation and evaluation tools from KCL Planning department and the planning community around the ICAPS conference).
227 lines • 11.1 kB
JavaScript
"use strict";
/* --------------------------------------------------------------------------------------------
* Copyright (c) Jan Dolejsi. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlanFunctionEvaluator = void 0;
const pddl_workspace_1 = require("pddl-workspace");
const ValStep_1 = require("./ValStep");
const ValueSeq_1 = require("./ValueSeq");
const PlanTimeSeriesParser_1 = require("./PlanTimeSeriesParser");
const valUtils_1 = require("./valUtils");
const DOMAIN_PREFIX = "domain";
const PROBLEM_PREFIX = "problem";
const PLAN_PREFIX = "plan";
/** Evaluates numeric function values in the course of the plan. */
class PlanFunctionEvaluator {
/**
* Constructs
* @param plan plan to evaluate
* @param options options
*/
constructor(plan, options) {
this.plan = plan;
this.options = options;
if (!this.plan.domain || !this.plan.problem) {
throw new ReferenceError("Plan is missing domain or problem.");
}
this.domain = this.plan.domain;
this.problem = this.plan.problem;
this.grounder = new pddl_workspace_1.Grounder(this.plan.domain, this.plan.problem);
this.allConstantsAndObjects = this.domain.getConstants().merge(this.problem.getObjectsTypeMap());
}
/**
* @returns `true` if the underlying utilities are available.
*/
isAvailable() {
var _a;
return !!((_a = this.options) === null || _a === void 0 ? void 0 : _a.valueSeqPath) && !!this.options.valStepPath;
}
getValStepPath() {
var _a;
return (_a = this.options) === null || _a === void 0 ? void 0 : _a.valStepPath;
}
/**
* Evaluates the functions individually, or in groups by lifted function.
*/
evaluate() {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
const domainFile = yield valUtils_1.Util.toPddlFile(this.domain.getCompiledText(), { prefix: DOMAIN_PREFIX });
const problemFile = yield valUtils_1.Util.toPddlFile(this.problem.getCompiledText(), { prefix: PROBLEM_PREFIX });
const planFile = yield valUtils_1.Util.toPddlFile(this.plan.getText(), { prefix: PLAN_PREFIX });
const chartData = new Map();
const changingGroundedFunctions = yield this.getChangingGroundedFunctions();
const changingFunctionsGrouped = ((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.shouldGroupByLifted) !== null && _b !== void 0 ? _b : true)
? this.groupByLifted(changingGroundedFunctions)
: this.doNotGroupByLifted(changingGroundedFunctions);
const liftedFunctions = Array.from(changingFunctionsGrouped.keys());
yield Promise.all(liftedFunctions.map((liftedFunction) => __awaiter(this, void 0, void 0, function* () {
const groundedFunctions = changingFunctionsGrouped.get(liftedFunction);
if (groundedFunctions) {
yield this.tryAddChartValues(domainFile, problemFile, planFile, liftedFunction, groundedFunctions, chartData);
}
})));
return chartData;
});
}
evaluateMetrics() {
return __awaiter(this, void 0, void 0, function* () {
const domainFile = yield valUtils_1.Util.toPddlFile(this.domain.getCompiledText(), { prefix: DOMAIN_PREFIX });
const problemFile = yield valUtils_1.Util.toPddlFile(this.problem.getCompiledText(), { prefix: PROBLEM_PREFIX });
const planFile = yield valUtils_1.Util.toPddlFile(this.plan.getText(), { prefix: PLAN_PREFIX });
return yield new ValueSeq_1.ValueSeq(domainFile, problemFile, planFile, this.options).evaluateMetric();
});
}
evaluateExpressionInputs(expression) {
return __awaiter(this, void 0, void 0, function* () {
const inputVariables = expression.getVariables();
const domainFile = yield valUtils_1.Util.toPddlFile(this.domain.getCompiledText(), { prefix: DOMAIN_PREFIX });
const problemFile = yield valUtils_1.Util.toPddlFile(this.problem.getCompiledText(), { prefix: PROBLEM_PREFIX });
const planFile = yield valUtils_1.Util.toPddlFile(this.plan.getText(), { prefix: PLAN_PREFIX });
return yield new ValueSeq_1.ValueSeq(domainFile, problemFile, planFile, this.options).evaluate(inputVariables);
});
}
evaluateExpression(expression) {
return __awaiter(this, void 0, void 0, function* () {
const inputValues = yield this.evaluateExpressionInputs(expression);
const fv = new PlanTimeSeriesParser_1.FunctionValues(new pddl_workspace_1.Variable("~expression"));
if (inputValues.size === 0) {
const context = new StaticEvaluationContext();
const constantValue = expression.evaluate(context);
const constantDefinedValue = constantValue === undefined ? NaN : constantValue;
fv.addValue(0, constantDefinedValue);
fv.addValue(this.plan.makespan, constantDefinedValue);
}
else {
const context = new ValueSeqEvaluationContext(inputValues);
const firstInput = [...inputValues.values()][0];
for (let index = 0; index < firstInput.values.length; index++) {
const time = firstInput.getTimeAtIndex(index);
context.setTime(time);
const valueAtTime = expression.evaluate(context);
fv.addValue(time, valueAtTime !== undefined ? valueAtTime : NaN);
}
}
return fv;
});
}
groupByLifted(variables) {
const grouped = new Map();
variables.forEach(var1 => {
const lifted = this.domain.getLiftedFunction(var1);
if (lifted) {
const grounded = grouped.get(lifted);
if (grounded) {
grounded.push(var1);
}
else {
grouped.set(lifted, [var1]);
}
}
});
return grouped;
}
doNotGroupByLifted(variables) {
const grouped = new Map();
variables.forEach(var1 => grouped.set(var1, [var1]));
return grouped;
}
getChangingGroundedFunctions() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.valStepPath)) {
return [];
}
const happenings = pddl_workspace_1.PlanInfo.getHappenings(this.plan.steps);
const finalStateValues = yield new ValStep_1.ValStep(this.domain, this.problem)
.executeBatch(happenings, this.options);
if (!finalStateValues) {
return [];
} // ValStep failed
return finalStateValues
.map(value => this.getFunction(value.getVariableName()))
.filter(variable => !!variable) // filter out null values
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.map(v => v);
});
}
getFunction(variableName) {
const variableNameFragments = variableName.split(" ");
const liftedVariableName = variableNameFragments[0];
const liftedVariable = this.domain.getFunction(liftedVariableName);
if (!liftedVariable) {
return null;
}
const objects = variableNameFragments.slice(1)
.map(objectName => { var _a; return (_a = this.allConstantsAndObjects.getTypeOfCaseInsensitive(objectName)) === null || _a === void 0 ? void 0 : _a.getObjectInstance(objectName); })
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
.filter(o => !!o).map(o => o);
return liftedVariable.ground(objects);
}
tryAddChartValues(domainFile, problemFile, planFile, liftedFunction, groundedFunctions, chartData) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.addChartValues(domainFile, problemFile, planFile, liftedFunction, groundedFunctions, chartData);
}
catch (err) {
console.log("Cannot get values for function " + liftedFunction.getFullName());
console.log(err);
}
});
}
addChartValues(domainFile, problemFile, planFile, liftedFunction, groundedFunctions, chartData) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
if (!((_a = this.options) === null || _a === void 0 ? void 0 : _a.valueSeqPath)) {
throw new Error('Check first Evaluator#isAvailable()');
}
if (groundedFunctions.length === 0) {
return;
}
const valueSeq = new ValueSeq_1.ValueSeq(domainFile, problemFile, planFile, this.options);
let values = yield valueSeq.evaluateForLifted(liftedFunction, groundedFunctions);
if (!values) { // it was either empty (no grounding) or constant
return;
}
if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.adjustDuplicatedTimeStamps) {
values = values.adjustForStepFunctions();
}
chartData.set(liftedFunction, values);
});
}
ground(variable) {
return this.grounder.ground(variable);
}
}
exports.PlanFunctionEvaluator = PlanFunctionEvaluator;
class StaticEvaluationContext {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
get(variableName) {
throw new Error("Method not implemented.");
}
}
class ValueSeqEvaluationContext {
constructor(values) {
this.values = values;
this.time = 0;
}
setTime(time) {
this.time = time;
}
get(variableName) {
const values = this.values.get(variableName);
return values === null || values === void 0 ? void 0 : values.getValue(this.time);
}
}
//# sourceMappingURL=PlanFunctionEvaluator.js.map