ltentrup / caqe

CAQE is a solver for quantified Boolean formulas
MIT License
29 stars 6 forks source link

Simple C API for embedding caqe #11

Closed rndmcnlly closed 3 years ago

rndmcnlly commented 3 years ago

I'm interested in using QBF solvers in interactive applications. When I've needed SAT or 2QBF solving in an interactive setting before, it has been convenient that there was both a Python (for prototyping) and JavaScript (for public deployment on the web) interface to the clasp answer-set solver. Rather than suggesting that the caqe project maintain multiple systems of bindings for high-level languages and weird compilation targets, I have a simpler solution.

Add a single new file, caqe/src/bin/libcaqe.rs (or whatever) that exports a single C function with the following signature (and update Cargo.toml to build an associated dylib):

const char* run_caqe(const char* args, const char* input);

It might go something like this. I've only been using Rust for like 3 days, but this advice looks relevant.

use std::ffi::CStr;
use std::os::raw::c_char;

#[no_mangle]
pub extern fn run_caqe(args: *const c_char, input: *const c_char) *const c_char {
    // args is whatever you'd have found from main's argv entries, joined by spaces
    // input is whatever you'd have read from the input file
    // return value is pointer to whatever you would have written to stdout
    ... // caller assumes ownership of memory referred to by returned pointer
}

Goals:

Non-goals:

How does this enable Python usage? Plain Python code can use the builtin ctypes library to dynamically link and call C functions from the native executable. The string-to-string API design here makes the glue code simple while implying some extra overhead for encoding/decoding to strings. Until we've got some example integrations, we don't know if trying to avoid this overhead is relevant. The point is that the language-specific glue code lives outside of the caqe project.

How does this enable JavaScript usage? By compiling Rust to WebAssembly, a browser-compatible library can be built with minimal changes to the codebase. Existing guidance for calling exported C functions will allow calling the one exported function above from plain JavaScript code. The fact that caqe internally uses C++-based CryptoMiniSat somewhat complicates the compile-to-WASM pipeline, but I think the relevant fixes only involve build scripts and not any Rust or C++ changes. Regardless, these build scripts and associated glue logic again live outside the caqe project.

People actually want a QBF solver in JavaScript??? Yes! The core mechanics of the browser-based game Proofdoku involves solving a 2-QBF problem during each main gameplay step. See the discussion of argument.lp in this paper. The game was made before WebAssembly emerged and the C++-to-JavaScript pipeline of the time didn't yield acceptable performance in mobile browsers. In 2021 (and now the solver maintainers provide pre-compiled WASM binaries), I'd happily have run the solver right inside of the browser in the same way I'd like to try using caqe. The problem instances that arise in interactive settings are usually very small, so making sure the solver can run on strange devices (phones, watches, refrigerators, etc.) and without the need to run a central solving service is usually more important than performance in this setting.

ltentrup commented 3 years ago

I like the idea of extending CAQE to a QBF solver library. There is a lot to unpack in this issue, so maybe it is a good idea to split it into smaller, more actionable items? I am not sure that this single function would suffice in your use case: don't you need the solver result, i.e., assignments? It is pretty easy to create Python bindings from Rust using https://crates.io/crates/pyo3. For WASM, I don't know because I have never tried it before.

rndmcnlly commented 3 years ago

Digging into it more, I agree; there's more to unpack. Some notes so far:

I figure out the basics of a C API in https://github.com/rndmcnlly/caqe (takes a borrowed string as input; returns an integer; no rich output or configuration of solver).

I'll close the issue in a few days if I can't figure out something more satisfying overall.

rndmcnlly commented 3 years ago

Given that I only proposed the C API as a means to two unrelated ends, I'll close the issue now.

Final notes: