/asp-chef-app

Primary LanguageSvelteApache License 2.0Apache-2.0

ASP Chef (alpha)

ASP Chef is a simple, intuitive web app for analysing answer sets without having to deal with complex tools or programming languages... well, excluding ASP!

ASP Chef is inspired by CyberChef, and as such it is designed to ease the creation of pipelines of operations over arrays of models rather than being an IDE or editor for ASP.

Below is a demo video:

IMAGE ALT TEXT HERE

Prerequisites

A modern browser! Firefox is a good choice as it has no restriction on the length of URLs. Large pipelines may hit the restriction on the URL length of other browsers due to the way ASP Chef encodes recipes in the URL.

No other software is needed as everything is run in the browser thanks to SvelteKit, clingo-wasm, and many other Javascript libraries.

Usage

The UI comprises three vertical panels, namely the Operations panel, the Recipe panel and the I/O panel.

ASP Chef recipes constitute a pipeline where each step takes in input an array of models and provides in output an array of models; actually, a model in this context is any valid output of ASP systems, that is, even ground terms can be elements of a model.

The recipe is composed by selecting ingredients from the Operations panel and setting their options in the Recipe panel. Popovers are shown for each operation to provide a minimal explanation on how to use it in a recipe.

The pipeline starts by splitting the input of the recipe (given in the I/O panel) on occurrences of the reserved character §. Each part of the input is parsed and checked to be a valid model. Upon termination, the output of the last step of the recipe is shown in the I/O panel.

The recipe and its input are encoded in the URL, which can be used to restore the recipe and its input for future usage. As the list of operations grows, and the operations are extended and modified, it is possible that a previously crafted recipe becomes incompatible with the current deployed version of ASP Chef. While it is possible that in the future a mechanism to preserve retro-compatibility will be implemented, it must be noted that at the moment the main goal of ASP Chef is to ease the fast prototyping of ASP pipelines rather than their long terming usage.

Examples

Billy the Kid

A problem from the LP/CP Programming Contest 2019. It is described here. An ASP Chef recipe solving the problem and including a graphical representation of intermediate steps can be found here.

Aquarium

A problem from the LP/CP Programming Contest 2020. It is described here. An ASP Chef recipe solving the problem and including a graphical representation of the solution can be found here

Buttons and Scissors

A problem from the LP/CP Programming Contest 2021. It is described here. An ASP Chef recipe solving the problem and including a graphical representation of intermediate steps can be found here.

Fortress

A problem from the LP/CP Programming Contest 2022. It is described here. An ASP Chef recipe solving the problem and including a graphical representation of the solution can be found here

Development

Setup

Checkout the repository and run

$ npm install             # install dependencies
$ npx playwright install  # for E2E test automation

Link clingo.wasm as a static file with

$ mkdir -p static/dist; ln -s ../../node_modules/clingo-wasm/dist/clingo.wasm static/dist/

Start the development server on port 5188 with

$ npm run dev

Run E2E tests with one of the following commands:

$ npm run test                                 # all tests are run headless
$ npm run test tests/test.Something.ts         # tests from a single file
$ npm run test:headed                          # all tests, show the browser
$ npm run test:headed tests/test.Something.ts  # single file, show the browser

Adding new operations

Create a component in src/lib/operations and link it in the component <RecipePanel>.

The operation must be associated with a unique name. The file implementing the operation must be named as the operation itself, spaces excluded.

The operation must define its default options and register itself in the Recipe class. When instantiated as an ingredient, the operation will receive in input an array of models, and it is expected to produce in output an array of models. If the operation must show something depending on the input it will receive, listeners must be used (see the Output operation).

The DOM content of the operation should just instantiate the generic <Operation> component with a minimal description to be shown as a popover and the content to show when the operation is added as an ingredient.

To ease test automation it is strongly suggested to add data-testid attributes to I/O elements of interest. To actually test an operation add a file in tests/test.OperationName.ts with the relevant testcases. Again, to ease the testing of combinations of different operations implement a method in tests/utils.ts to add the operation as an ingredient with the provided options.

How to implement an external server

The Server operation interacts with a remote or local server. The actual server can implement any function and is required to answer to a POST request with JSON content. The JSON content is an object with properties input_part, decoded_input and options.

A request is done for each model in input, where input_part contains the list of atoms (as strings), decoded_input contains the content decoded from some Base64 predicate, and options is a string given in the ingredient. It is up to the server to give a meaning to these elements, and to produce in response a JSON object with property models.

A minimalist example of server searching for stable models via the Python API of clingo is given below.

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware

import clingo


app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5188", "https://asp-chef-alpha.netlify.app"],
    allow_credentials=False,
    allow_methods=["POST"],
    allow_headers=["*"],
)


@app.post("/")
async def process(request: Request):
    json = await request.json()
    input_part = json["input_part"]
    decoded_input = json["decoded_input"]
    options = json["options"]
    
    control = clingo.Control(options)
    program = '\n'.join([f"{atom}." for atom in input_part]) + '\n'.join(decoded_input)
    control.add(program)
    control.ground([("base", [])])
    res = []
    control.solve(on_model=lambda model: res.append([str(atom) for atom in model.symbols(shown=True)]))
    return {"models" : res}

If the above script is saved in the file clingo_server.py, the server can be run with

$ uvicorn clingo_server:app

As another example, a server relying on MiniZinc can be implemented as follows:

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware

from minizinc import Instance, Model, Solver

import re


app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5188", "https://asp-chef-alpha.netlify.app"],
    allow_credentials=False,
    allow_methods=["POST"],
    allow_headers=["*"],
)

constant_pattern = re.compile('^constant\((?P<name>\w+),(?P<value>\d+)\)$')


@app.post("/")
async def process(request: Request):
    json = await request.json()
    input_part = json["input_part"]
    decoded_input = json["decoded_input"]
    options = json["options"]
    
    solver = Solver.lookup("gecode")
    model = Model()
    model.add_string('\n'.join(decoded_input))
    instance = Instance(solver, model)
    for atom in input_part:
        match = constant_pattern.match(atom)
        if match:
            name = match.group('name')
            value = int(match.group('value'))
            instance[name] = value
            
    res = await instance.solve_async()
    res = list(f"{options}({index},{value})" for index, value in enumerate(res[options], start=1))
    return {"models" : [res]}

The above script interprets the encoded content as a MiniZinc model (the analogous of a program in ASP), instances of predicate constant/2 as integer constants, and the content of options as the name of the variable to return in output.

Contributors