Skip to content
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

~~special~~ indispensable math functions #2765

Open
dcnorris opened this issue Jan 9, 2025 · 10 comments
Open

~~special~~ indispensable math functions #2765

dcnorris opened this issue Jan 9, 2025 · 10 comments

Comments

@dcnorris
Copy link

dcnorris commented Jan 9, 2025

Scryer would be much more usable for my work if it natively supported so-called 'special' math functions, such as those implemented in Rust crate https://github.com/Axect/puruspe. (They are 'special' really only due to the special effort involved in getting access to them!)

How would people feel about supporting at least the Error, Gamma, and Beta groups of functions from puruspe?

(I did manage to hack arithmetic.rs, machine/arithmetic_ops.rs and machine/dispatch.rs to introduce a new Erf Instruction, albeit mapped to sin; I got a profusion of compiler errors in my attempts to substitute erf in place of sin. But that was all flying blind, without really understanding the design, and how transcendental functions are fitting in to Instructions. Perhaps it will be best to document that misadventure in a separate Discussion.)

@triska
Copy link
Contributor

triska commented Jan 9, 2025

For Scryer to become a reference system for testing standards conformity of Prolog code, there would have to be a way to turn these extensions off (so-called strictly conforming mode).

To avoid weighing down the engine core (i.e., arithmetic evaluation), it would be better to provide such (conforming) extensions in a dedicated library, like the arithmetic functions in arithmetic.pl.

@dcnorris
Copy link
Author

dcnorris commented Jan 9, 2025

I'll try that, then. Should be a lot easier than hacking the core!

@triska
Copy link
Contributor

triska commented Jan 9, 2025

Maybe this could be useful as an example of how to make Rust functionality available as a predicate: #2245

@adri326
Copy link
Contributor

adri326 commented Jan 9, 2025

There's also some info on the wiki (one of the few things currently there): https://github.com/mthom/scryer-prolog/wiki/Flying-Roll-%231:-instructions.rs-and-the-Dispatch-Loop :)

You unfortunately have to put the glue code in dispatch.rs, but from there on you can put the rest of the implementation somewhere else — if you want full opt-in/opt-out support, that could be in a module that is only imported if a corresponding feature flag is enabled.

@dcnorris
Copy link
Author

dcnorris commented Jan 9, 2025

By slavishly following your commit @triska, I've managed in less than 2 hours to hook up an erf/2 that returns a bogus f64 result. The remaining task is I think getting the f64 out of arg 1, and running it thru puruspe::error::erf before unifying with return_value, in this function from machine/system_calls.rs:

    #[cfg(feature = "special-math")]
    #[inline(always)]
    pub(crate) fn erf(&mut self) {
        let return_value = self.deref_register(2);
        let bogus_ans = float_alloc!(0.123f64, self.machine_st.arena);
        self.machine_st.unify_f64(bogus_ans, return_value)
    }
?- use_module(library(special)).
   true.
?- erf(1.0, Erf).
   Erf = 0.123.

(Any pointers to example code that gets an f64 out of a register are welcome!)

@triska
Copy link
Contributor

triska commented Jan 9, 2025

Maybe sleep/1 can serve as an example for processing a numeric argument and in particular a float:

let time = match Number::try_from(time) {

You can move type checking to the Prolog side of the implementation, which makes it simpler.

@dcnorris
Copy link
Author

That did the trick, thanks @triska!

?- use_module(library(special)).
   true.
?- erf(1.1, Erf).
   Erf = 0.8802050695740817.
julia> import SpecialFunctions.erf

julia> erf(1.1)
0.8802050695740817

@dcnorris
Copy link
Author

dcnorris commented Jan 10, 2025

dcnorris@157c35c implements an erf/2 predicate in a math/special library.

  • Are numerical libraries of this sort out-of-scope for Scryer, or inconsistent with its aims?
  • If not, I'd gladly receive feedback on everything here, from naming to documentation to Rust style, before I churn out the whole library.
  • One particular point of interest will be (I think) doing all the domain checking on the Prolog side, to avoid Rust panics for out-of-range parameters. I'd be glad for pointers to Prolog code that handles floating-point range checks excellently.
?- use_module(library(math/special)).
   true.
?- erf(0.5, Erf).
   Erf = 0.5204998778130465.

@dcnorris
Copy link
Author

dcnorris@7b2159e now modularizes the contribution nicely (IMO), thanks to guidance from @adri326. (Quite remarkable how the Rust compiler's suggestions carried me through this refactoring.)

dcnorris referenced this issue in dcnorris/scryer-prolog Jan 11, 2025
@triska
Copy link
Contributor

triska commented Jan 11, 2025

To clarify one point that I now see was not made sufficiently clear: The point of implementing such extensions in Prolog libraries is that Scryer starts in the strictly conforming mode. Only when the library is loaded do these predicates become available. There is no reason to make the Rust implementation contingent on flags, except for cases where compilation would otherwise fail on some platforms and it may be an advantage to compile Scryer itself without this functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants