UNPKG

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
"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