-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Programatic use of Machine / Scryer as library #1880
Conversation
…tput() -> String. Make read_term_from_user_input() handle Stream::Byte.
Very interesting work and contribution, thank you a lot for working on this! The only suggestion I have is that it would be awesome, and I think preferable, to generalize the currently existing function scryer-prolog/src/machine/mod.rs Line 307 in 112d398
Generalizing this function so that an arbitrary goal can be specified could let you avoid the introduction of the new and seemingly rather ad hoc definition of Personally, I would prefer to not add such additional toplevels to |
Ok, I've generalized I've also extracted the lib query functions and test into their own file. Also added a new |
BTW, I was basing these changes off of the v0.9.1 commit because the latest head with updated tokio dependency creates problems when used in my project which also pulls in Deno with specific dependencies which work with that old revision of scryer but not the latest head. |
If user-interaction is not needed, a simpler toplevel could suffice. For instance, here is a very rudimentary toplevel that may suffice for your use case: toplevel :- read_term(Goal, [variable_names(VNs)]), Goal, write('bindings(['), write_bindings(VNs), write(']).'), nl, false. toplevel :- toplevel. write_bindings([]). write_bindings([VN|VNs]) :- write_bindings_(VNs, VN). write_bindings_([], VN) :- write_binding(VN). write_bindings_([VN|VNs], Prev) :- write_binding(Prev), write(','), write_bindings_(VNs, VN). write_binding(Var=Val) :- write(Var), write(=), write_term(Val, [quoted(true),double_quotes(true)]). Sample interaction: ?- toplevel. X=3. bindings([X=3]). member(X, "abc"). bindings([X=a]). bindings([X=b]). bindings([X=c]). Add Such a rudimentary toplevel may be provided by the application itself. |
…f write_eq to avoid truncation of results
625c2f0
to
9fd6e18
Compare
If I read the CI logs correctly, only the build for the (comparatively dated) 20.04 version of Ubuntu fails, and only for the 32-bit version? If that is the case, then I do not consider the single failing CI build a justification for any delay with merging this PR: Seconding #1880 (comment), I would recommend to first and foremost get the API right, so that (as mentioned above) answers can be accessed by an iterator, because then also infinite sequences of answers can be processed in programs that use this API. (Think of |
This failure and #2126 appear unrelated to me.
Both of these two steps appear to only run for the |
I measured memory usage of |
Was on the master branch instead of the PR branch. |
Notably, the github actions runners have 7GB of memory: |
I'm not familiar with this part of rust, but is it possible to reduce the number of inlines during compilation? There seems to be a loooot of them. |
fwiw, I tried compiling with # I ran this in another terminal during compilation. It's not scientific at all, but I can't be bothered to try harder when the results are so clear already.
while true; do smem -t -P '(cargo|rustc) ' | tail -1 | awk '{print $5}' >> log.txt; sleep 0.1s; done |
Ok I made some changes on my fork so the CI gets farther, but there are still some issues which I think are related to platform-specific code that doesn't work in wasm32. In particular, running cargo test on the wasm target fails:
My guess is that some tests need to be excluded for the wasm target? |
Changes to CI in #2137 may have fixed the build issues here. (Note: a new build will be triggered if you push any commit or amend the last commit and force push it.) While developing that pr I found that tests are failing for the wasm build and it probably needs some conditional build tags on tests. See this issue for details: |
I can also re-run it as the repo owner, which I'm doing now. |
Wouln't it need to be rebased on master/merged with master first, as CI is testing the state of the PR not the merged state? |
I think the failing build on ubuntu-20.04 nightly is due to f80dff8 not yet being applied in this branch. |
A push to the branch is handled differently than manually triggering a rebuild. Specifically, if triggered by the |
Right, of course. My enthusiasm for merging this PR got in the way of my sense. |
I've merge master in, which includes that commit. Still failing... |
Different Error though, now in step
|
instead of target_os = “wasi”
Great! If there are no further comments I'll merge and we'll have a new release out in time for the meetup. |
🎉 🥳 🚀 (I didn't get to changing |
Keep in mind what I said about breaking changes before. If the current version gets on crates.io, which is the next logical step, it will have to follow the Rust variant of semver. Changing the signature of |
I think this could be a good way to do benchmarking with scryer-prolog. See discussion: #1782 With iai 1, which measures performance using Cachegrind by counting instructions and cache misses, this could be viable to set up in CI. Thoughts? Example:
use iai::{black_box, main};
fn iai_benchmark_edges(n: u64) -> u64 {
let mut machine = Machine::new_lib();
machine.load_module_string("facts", String::from(r#"
:- use_module(library(clpb)).
:- use_module(library(assoc)).
:- use_module(library(lists)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Contiguous United States and DC as they appear in SGB:
http://www-cs-faculty.stanford.edu/~uno/sgb.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
edge(al, fl).
edge(al, ga).
edge(al, ms).
edge(al, tn).
edge(ar, la).
edge(ar, mo).
edge(ar, ms).
edge(ar, ok).
edge(ar, tn).
edge(ar, tx).
edge(az, ca).
edge(az, nm).
edge(az, nv).
edge(az, ut).
edge(ca, nv).
edge(ca, or).
edge(co, ks).
edge(co, ne).
edge(co, nm).
edge(co, ok).
edge(co, ut).
edge(co, wy).
edge(ct, ma).
edge(ct, ny).
edge(ct, ri).
edge(dc, md).
edge(dc, va).
edge(de, md).
edge(de, nj).
edge(de, pa).
edge(fl, ga).
edge(ga, nc).
edge(ga, sc).
edge(ga, tn).
edge(ia, il).
edge(ia, mn).
edge(ia, mo).
edge(ia, ne).
edge(ia, sd).
edge(ia, wi).
edge(id, mt).
edge(id, nv).
edge(id, or).
edge(id, ut).
edge(id, wa).
edge(id, wy).
edge(il, in).
edge(il, ky).
edge(il, mo).
edge(il, wi).
edge(in, ky).
edge(in, mi).
edge(in, oh).
edge(ks, mo).
edge(ks, ne).
edge(ks, ok).
edge(ky, mo).
edge(ky, oh).
edge(ky, tn).
edge(ky, va).
edge(ky, wv).
edge(la, ms).
edge(la, tx).
edge(ma, nh).
edge(ma, ny).
edge(ma, ri).
edge(ma, vt).
edge(md, pa).
edge(md, va).
edge(md, wv).
edge(me, nh).
edge(mi, oh).
edge(mi, wi).
edge(mn, nd).
edge(mn, sd).
edge(mn, wi).
edge(mo, ne).
edge(mo, ok).
edge(mo, tn).
edge(ms, tn).
edge(mt, nd).
edge(mt, sd).
edge(mt, wy).
edge(nc, sc).
edge(nc, tn).
edge(nc, va).
edge(nd, sd).
edge(ne, sd).
edge(ne, wy).
edge(nh, vt).
edge(nj, ny).
edge(nj, pa).
edge(nm, ok).
edge(nm, tx).
edge(nv, or).
edge(nv, ut).
edge(ny, pa).
edge(ny, vt).
edge(oh, pa).
edge(oh, wv).
edge(ok, tx).
edge(or, wa).
edge(pa, wv).
edge(sd, wy).
edge(tn, va).
edge(ut, wy).
edge(va, wv).
pairs_keys_values([], [], []).
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
pairs_keys_values(ABs, As, Bs).
independent_set(*(NBs)) :-
findall(U-V, edge(U, V), Edges),
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
pairs_keys_values(Pairs, Nodes, _),
list_to_assoc(Pairs, Assoc),
maplist(not_both(Assoc), Edges, NBs).
not_both(Assoc, U-V, ~BU + ~BV) :-
get_assoc(U, Assoc, BU),
get_assoc(V, Assoc, BV).
"#));
let output: QueryResult = machine.run_query(String::from(r#"independent_set(Sat), sat_count(Sat, Count)."#));
assert_eq!(output, QueryResult::Matches(vec![
QueryMatch::from(btreemap!{
"Count" => Value::from("217968"),
}),
]));
}
iai::main!(iai_benchmark_edges); Footnotes |
I think I just discovered this too early before you've had the chance to write it, but as far as I can tell there's no reference to the possibility of using this as an embedded library on the README or the documentation (or this PR). Want to just drop a note of in support of promoting this use-case because it's exactly what I came here looking for, and I think it's a killer feature! |
These changes make it possible to run Prolog queries on a
Machine
instance in a 3rd-party crate that pulls-in Scryer as a library dependency, interfacing with the machine only with in-memory Strings.Overview
Machine::run_input_once(&mut self)
runs the added toplevel predicaterun_input_once/0
which reads the query from user input likerepl
but just executes it once, returning/printing all resultsMachine::load_module_string(&mut self, module_name: &str, program: String)
creates a stream from the given string and loads like a fileMachine::set_user_input(&mut self, input: String)
andMachine::get_user_output(&self) -> String
enabling interfacing with the machineMachine::run_query(&mut self, query: String) -> QueryResult
wraps it all up by setting the query as input and parsing the output string into added Rust typeQueryResult
.Example usage
Next steps
consult/1
like updating of machine state