3.0370370367037037036703703703670 / 123456789012345678901234567890
3.0370370367037037036703703703671 / 123456789012345678901234567890
I am playing with the idea to introduce two types
of float of integer division:
- a) Fast and less precise: A/B basically computes
float(A)/float(B).
- b) Slow and more precise: float(A rdiv B) is then
the more precise counterpart.
Currently there is no unity among Prolog systems.
When (/)/2 appears, some Prolog systems implement
a) and some implement b). And it appears
implementing b) is hard! I get:
/* SWI-Prolog 8.5.17 */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.
?- N9 is 370370367037037036703703703671 / 123456789012345678901234567890.
N9 = 2.9999999999999996.
Python doesn’t have this bug:
/* Python 3.11.0rc1 (main, Aug 8 2022, 11:30:54) */
3.0370370367037037036703703703670 / 123456789012345678901234567890
3.0370370367037037036703703703671 / 123456789012345678901234567890
Under certain circumstances, there is also an ultra fast
rounding for rational numbers, similar like there is for bigint.
Take this bigint approach, assuming msb(X) > 52:
float_half_even(X,Y) :-
M is msb(X),
float_half_even(M, X, Y).
float_half_even(M, X,Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X)), !, mpz_get_d(X,Y). float_half_even(_, X,Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Now we can do the same for rational numbers, namely
we can do the following, now assuming msb(A)-msb(B) > 52,
we convert a rational number A rdiv B to a float as follows:
float_rat_half_even(A,B,Y) :-
divmod(A,B,X,Z),
M is msb(X),
float_rat_half_even(M, X, Z, Y).
float_rat_half_even(M, X, Z, Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X), Z =:= 0), !, mpz_get_d(X,Y). float_rat_half_even(_, X, _, Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Will do some testing. The testing would be easier if SWI-Prolog
(/)/2 were not buggy. So I have to do some testing against
some other Prolog system, which one?, or against Python (/)/2.
4.7148154113124655e+1915716035654990179394637077 / 333333
If we have a numerator with a high lsb/1,
i.e. a lot of zeros in it, the algorithm
indeed agrees, namely I find the following result:
/* SWI-Prolog 8.5.17 */
?- B is 333333, between(1,1000000,N), A is N<<80, X is A/B, float_rat_half_even(A,B,Y), X =\= Y.
false.
Hell breaks loose if the numerator is not that
simple, but there are only a few cases where
things go wrong. Need to cross check with Python:
/* SWI-Prolog 8.5.17 */
?- B is 333333, between(1,1000000,N), A is N<<80+123456789, X is A/B, float_rat_half_even(A,B,Y), X =\= Y, write(N), nl, fail; true.
13
26
52
8915
458442
791775
true.
Mostowski Collapse wrote:
Under certain circumstances, there is also an ultra fast
rounding for rational numbers, similar like there is for bigint.
Take this bigint approach, assuming msb(X) > 52:
float_half_even(X,Y) :-
M is msb(X),
float_half_even(M, X, Y).
float_half_even(M, X,Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X)), !, mpz_get_d(X,Y).
float_half_even(_, X,Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Now we can do the same for rational numbers, namely
we can do the following, now assuming msb(A)-msb(B) > 52,
we convert a rational number A rdiv B to a float as follows:
float_rat_half_even(A,B,Y) :-
divmod(A,B,X,Z),
M is msb(X),
float_rat_half_even(M, X, Z, Y).
float_rat_half_even(M, X, Z, Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X), Z =:= 0), !,
mpz_get_d(X,Y).
float_rat_half_even(_, X, _, Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Will do some testing. The testing would be easier if SWI-Prolog
(/)/2 were not buggy. So I have to do some testing against
some other Prolog system, which one?, or against Python (/)/2.
Cross checking with Python tells me (/)/2 is wrong
and float_rat_half_even/2 is correct (wao! lucky me),
for example for N=13 I get:
/* Python 3.11.0rc1 */
4.7148154113124655e+1915716035654990179394637077 / 333333
/* SWI-Prolog 8.5.7 */
?- X is 15716035654990179394637077 / 333333.
X = 4.714815411312465e+19. /* wrong too small */
?- float_rat_half_even(15716035654990179394637077, 333333, Y).
Y = 4.7148154113124655e+19.
Mostowski Collapse wrote:
If we have a numerator with a high lsb/1,
i.e. a lot of zeros in it, the algorithm
indeed agrees, namely I find the following result:
/* SWI-Prolog 8.5.17 */
?- B is 333333, between(1,1000000,N), A is N<<80, X is A/B, float_rat_half_even(A,B,Y), X =\= Y.
false.
Hell breaks loose if the numerator is not that
simple, but there are only a few cases where
things go wrong. Need to cross check with Python:
/* SWI-Prolog 8.5.17 */
?- B is 333333, between(1,1000000,N), A is N<<80+123456789, X is A/B, float_rat_half_even(A,B,Y), X =\= Y, write(N), nl, fail; true.
13
26
52
8915
458442
791775
true.
Mostowski Collapse wrote:
Under certain circumstances, there is also an ultra fast
rounding for rational numbers, similar like there is for bigint.
Take this bigint approach, assuming msb(X) > 52:
float_half_even(X,Y) :-
M is msb(X),
float_half_even(M, X, Y).
float_half_even(M, X,Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X)), !, mpz_get_d(X,Y).
float_half_even(_, X,Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Now we can do the same for rational numbers, namely
we can do the following, now assuming msb(A)-msb(B) > 52,
we convert a rational number A rdiv B to a float as follows:
float_rat_half_even(A,B,Y) :-
divmod(A,B,X,Z),
M is msb(X),
float_rat_half_even(M, X, Z, Y).
float_rat_half_even(M, X, Z, Y) :-
(getbit(X, M-53) =:= 0;
getbit(X, M-52) =:= 0, M-53 =:= lsb(X), Z =:= 0), !,
mpz_get_d(X,Y).
float_rat_half_even(_, X, _, Y) :-
mpz_get_d(X,H),
Y is nexttoward(H, 1E300).
Will do some testing. The testing would be easier if SWI-Prolog
(/)/2 were not buggy. So I have to do some testing against
some other Prolog system, which one?, or against Python (/)/2.
Ok, this was settled with Python mode in SWI-Prolog,
we can switch on more precise integer division. Another
application of Python mode, since it also applies to (**)/2,
measure failures of the power function:
% swi_sweep
swi_sweep :-
set_prolog_flag(prefer_rationals, true),
set_prolog_flag(max_rational_size, 0), set_prolog_flag(max_rational_size_action, float),
fail.
swi_sweep :- % like in needle3
L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep1, swi: '), write(C), nl, fail.
swi_sweep :- % like in fuzzer6
L is -(1<<12), H is (1<<12)+1,
M is -(1<<6), J is (1<<6)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep2, swi: '), write(C), nl, fail.
swi_sweep :-
set_prolog_flag(prefer_rationals, false),
fail.
swi_sweep.
Here are some test results. WASM is again the best, although
not extremly far away from WSL2. Windows is again last:
/* SWI-Prolog 8.5.20 WASM */
sweep1, swi: 2186
sweep2, swi: 1242
/* SWI-Prolog 8.5.20 WSL2 */
sweep1, swi: 3300
sweep2, swi: 1370
/* SWI-Prolog 8.5.20 Windows */
sweep1, swi: 405161
sweep2, swi: 786699
Mostowski Collapse schrieb am Freitag, 28. Oktober 2022 um 22:16:57 UTC+2:
Ok, this was settled with Python mode in SWI-Prolog,
we can switch on more precise integer division. Another
application of Python mode, since it also applies to (**)/2,
measure failures of the power function:
% swi_sweep
swi_sweep :-
set_prolog_flag(prefer_rationals, true),
set_prolog_flag(max_rational_size, 0), set_prolog_flag(max_rational_size_action, float),
fail.
swi_sweep :- % like in needle3
L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep1, swi: '), write(C), nl, fail.
swi_sweep :- % like in fuzzer6
L is -(1<<12), H is (1<<12)+1,
M is -(1<<6), J is (1<<6)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep2, swi: '), write(C), nl, fail.
swi_sweep :-
set_prolog_flag(prefer_rationals, false),
fail.
swi_sweep.
To bring some of my precise float emulations to Dogelog
Playrer I need msb/1. I looking for inspiration.
Scryer Prolog isn't some inspiration:
test(X) :- msb(698072381837257563305573971580726935040345973 88060083583912810633489137842647022803107007382 69827592504647436506289493205329736363655295902 94743735953400982487450103250905033780625181457 49473537073305602444617606242084853904869693687
541, X).
?- time((between(1,1000,_), test(_), fail; true)).
% CPU time: 0.277s
true.
On the other hand Trealla Prolog does a good job:
/* Trealla Prolog */
test(X) :- X is msb(698072381837257563305573971580726935040345973 88060083583912810633489137842647022803107007382 69827592504647436506289493205329736363655295902 94743735953400982487450103250905033780625181457 49473537073305602444617606242084853904869693687
541).
?- time((between(1,1000,_), test(_), fail; true)).
Time elapsed 0.000384s
true.
Holy Cow, thats factor 721x slower. Looks like Scyer Prolog
msb/1 is O(N) where N is the number of bits, and Trealla
Prologs msb/1 is O(1). How comes?
Mostowski Collapse schrieb am Freitag, 28. Oktober 2022 um 22:18:13 UTC+2:
Here are some test results. WASM is again the best, although
not extremly far away from WSL2. Windows is again last:
/* SWI-Prolog 8.5.20 WASM */
sweep1, swi: 2186
sweep2, swi: 1242
/* SWI-Prolog 8.5.20 WSL2 */
sweep1, swi: 3300
sweep2, swi: 1370
/* SWI-Prolog 8.5.20 Windows */
sweep1, swi: 405161
sweep2, swi: 786699
Mostowski Collapse schrieb am Freitag, 28. Oktober 2022 um 22:16:57 UTC+2:
Ok, this was settled with Python mode in SWI-Prolog,
we can switch on more precise integer division. Another
application of Python mode, since it also applies to (**)/2,
measure failures of the power function:
% swi_sweep
swi_sweep :-
set_prolog_flag(prefer_rationals, true), set_prolog_flag(max_rational_size, 0), set_prolog_flag(max_rational_size_action, float),
fail.
swi_sweep :- % like in needle3
L is -(1<<15), H is (1<<15)+1,
M is -(1<<3), J is (1<<3)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep1, swi: '), write(C), nl, fail.
swi_sweep :- % like in fuzzer6
L is -(1<<12), H is (1<<12)+1,
M is -(1<<6), J is (1<<6)+1,
aggregate_all(count,
(between(L, H, P), P =\= 0,
between(M, J, Q), P**Q =\= float(P)**float(Q)), C),
write('sweep2, swi: '), write(C), nl, fail.
swi_sweep :-
set_prolog_flag(prefer_rationals, false),
fail.
swi_sweep.
Could emulate msb/1. So result for (**)(2 for other Prolog systems.
It seems PyPy and SICStus use the same pow() function:
System sweep1 sweep2 Total Variant
jekejeke 1756 666 2422 JDK 19
swi 2186 1242 3428 WASM
scryer 3300 1370 4670 WSL2
dogelog 2930 2174 5104 PyPy
sicstus 2930 2174 5104 Windows
ciao 3300 324342 327642 WSL2
eclipse 345587 482199 827786 Windows
Ciao botched something in sweep2. ECLiPSe botched
both sweep1 and sweep2. Trealla Prolog I couldn’t
test because of some bigint bug. Ciao Playground
couldn’t test because of missing auto yield i.e timeout.
Do we really need libraries such as GMP? My
idea, a good Novacore would have fast arithmetic,
so that one can realize many functions in pure
Prolog itself. Like this here:
iroot(X, Y, Z):
The predicate succeeds in Z with the Y-th root of X.
Here are some benchmarking results:
/* SWI-Prolog 8.5.20 */
?- M is (2^32+3), X is M^3+1, time((between(1,100000,_),
iroot(X, 3, Y), fail; true)).
% 4,900,000 inferences, 2.172 CPU in 2.164 seconds (100% CPU, 2256115 Lips)
/* Trealla Prolog 2.5.13 */
?- M is (2^32+3), X is M^3+1, time((between(1,100000,_),
iroot(X, 3, Y), fail; true)).
Time elapsed 1.28s
/* Jekejeke Prolog 1.5.5 JDK 1.8 */
?- M is (2^32+3), X is M^3+1, time((between(1,100000,_),
iroot(X, 3, Y), fail; true)).
% Threads 718 ms, GC 7 ms, Up 738 ms (Current 11/03/22 16:27:11)
Mostowski Collapse schrieb am Samstag, 29. Oktober 2022 um 16:37:47 UTC+2:
SICStus Prolog can be also quite annoying.
It has msb/1 but no lsb/1? Holy Cow.
Thank god it can be easily bootstrapped:
lsb(X, N) :- N is msb(X /\ -X).
This might do for smallints. But is surely not
the most efficient for bigints.
Mostowski Collapse schrieb am Samstag, 29. Oktober 2022 um 02:06:49 UTC+2:
Could emulate msb/1. So result for (**)(2 for other Prolog systems.
It seems PyPy and SICStus use the same pow() function:
System sweep1 sweep2 Total Variant
jekejeke 1756 666 2422 JDK 19
swi 2186 1242 3428 WASM
scryer 3300 1370 4670 WSL2
dogelog 2930 2174 5104 PyPy
sicstus 2930 2174 5104 Windows
ciao 3300 324342 327642 WSL2
eclipse 345587 482199 827786 Windows
Ciao botched something in sweep2. ECLiPSe botched
both sweep1 and sweep2. Trealla Prolog I couldn’t
test because of some bigint bug. Ciao Playground
couldn’t test because of missing auto yield i.e timeout.
Now I have added a new decimal128 datatype to my formerly
Jekejeke Prolog system. Syntactically it starts with the prefix
0m, inspired by a the money datatype in some database systems.
I almost created a separate division operator ('/128')/2 but now
the ordinary division operator (/)/2 switches to decimal128, as
soon as one of its argumemts is decimal128:
?- X is 1/3.
X = 0.3333333333333333.
?- X is 0m1/3.
X = 0m0.3333333333333333333333333333333333.
Using a little Taylor expansion of arctan can now compute π:
?- X is pi.
X = 3.141592653589793.
?- X is pi128.
X = 0m3.141592653589793238462643383279503.
By implementing more trigonometric functions, can not only
provide them to the end-user. They also can serve as a test
case generator for ordinary float routines. I already
found an interesting test case:
/* SWI-Prolog 8.5.20 Windows */
?- X is atan(897/2048).
X = 0.4128202041682507.
/* SWI-Prolog 8.5.20 WSL2 */
?- X is atan(897/2048).
X = 0.41282020416825077.
The second result is more accurate, right?
Mostowski Collapse schrieb am Montag, 7. November 2022 um 02:26:09 UTC+1:
Now I have added a new decimal128 datatype to my formerly
Jekejeke Prolog system. Syntactically it starts with the prefix
0m, inspired by a the money datatype in some database systems.
I almost created a separate division operator ('/128')/2 but now
the ordinary division operator (/)/2 switches to decimal128, as
soon as one of its argumemts is decimal128:
?- X is 1/3.
X = 0.3333333333333333.
?- X is 0m1/3.
X = 0m0.3333333333333333333333333333333333.
I am currently implementing unums in pure Prolog. There is
already some progress, basic operations with HALF_EVEN
and then an atan function realization. It can be used to compute
pi, it has pre-defined the Euler formula 20*atan(1/7)+8*atan(3/79):
/* Compute pi with 53 Bit Precision*/
?- mp_pi(53, (M,E)), P is M*(2**E).
P = 3.141592653589793.
I have no clue how accurate the atan is. Unlike libBF it
doesn’t use Ziv iteration, but a dynamically estimated
fixed number of members of the Taylor expansion in a
Horner schema implementation.
Mostowski Collapse schrieb am Montag, 7. November 2022 um 02:27:38 UTC+1:
Using a little Taylor expansion of arctan can now compute π:
?- X is pi.
X = 3.141592653589793.
?- X is pi128.
X = 0m3.141592653589793238462643383279503.
By implementing more trigonometric functions, can not only
provide them to the end-user. They also can serve as a test
case generator for ordinary float routines. I already
found an interesting test case:
/* SWI-Prolog 8.5.20 Windows */
?- X is atan(897/2048).
X = 0.4128202041682507.
/* SWI-Prolog 8.5.20 WSL2 */
?- X is atan(897/2048).
X = 0.41282020416825077.
The second result is more accurate, right?
Mostowski Collapse schrieb am Montag, 7. November 2022 um 02:26:09 UTC+1:
Now I have added a new decimal128 datatype to my formerly
Jekejeke Prolog system. Syntactically it starts with the prefix
0m, inspired by a the money datatype in some database systems.
I almost created a separate division operator ('/128')/2 but now
the ordinary division operator (/)/2 switches to decimal128, as
soon as one of its argumemts is decimal128:
?- X is 1/3.
X = 0.3333333333333333.
?- X is 0m1/3.
X = 0m0.3333333333333333333333333333333333.
Originally this request filed for two types of
division in case the arguments are integer.
One less precise, which converts both arguments
first to float, and thus might loose some precision,
and another that performs something more
precise. Possibly the discussion led me to a
new arithmetic for my new decimal128 type,
which now behaves as folllows, the division operator
(/)/2 is now polymorphic:
?- X is 1/3.
X = 0.3333333333333333.
?- X is 1/0m3.
X = 0m0.3333333333333333333333333333333333.
The first division is the less precise double conversion
based division if the arguments are integer or double.
The second is also a less precise division, this
time based on quadruple conversion. The prefix 0m
is the syntax for the new upcoming quadruple type
in formerly Jekejeke Prolog.
We built the quadruple type polymorphism of (/)/2 and other
basic arithmetic operations into the core. Question was now
whether we can provide the same polymorphism for
quadruple mathematical functions as well. This lead
to a new module library(decimal/quad) which provides
exactly this polymorphism by overloading sqrt, sin, etc..
from the ISO core standard. Here is an example:
?- X is atan(1/3).
X = 0.3217505543966422.
?- X is atan(1/0m3).
X = 0m0.3217505543966421934014046143586613.
Mostowski Collapse schrieb am Samstag, 31. Dezember 2022 um 05:38:50 UTC+1:
Originally this request filed for two types of
division in case the arguments are integer.
One less precise, which converts both arguments
first to float, and thus might loose some precision,
and another that performs something more
precise. Possibly the discussion led me to a
new arithmetic for my new decimal128 type,
which now behaves as folllows, the division operator
(/)/2 is now polymorphic:
?- X is 1/3.
X = 0.3333333333333333.
?- X is 1/0m3.
X = 0m0.3333333333333333333333333333333333.
The first division is the less precise double conversion
based division if the arguments are integer or double.
The second is also a less precise division, this
time based on quadruple conversion. The prefix 0m
is the syntax for the new upcoming quadruple type
in formerly Jekejeke Prolog.
I am still fiddling around with (is)/2 implementation.
Formerly Jekejeke Prolog had traditionally automatic
bridging and tunneling. This is all going down the drain,
we want to become more GNU Prolog compatible. Novacore
should be as puritanical as possible, namely a Prolog core that
has an upbringing that disapproves the suggar laced world of
formerly Jekejeke Prolog! Funny discovery, not all Prolog
systems throw the same evaluable errors. Here a little
discrepancy between GNU Prolog and SWI-Prolog:
/* GNU Prolog */
?- X is append(1,2).
uncaught exception: error(type_error(evaluable,append/2),(is)/2)
?- X is append([1,2],[3]).
uncaught exception: error(type_error(evaluable,append/2),(is)/2)
/* SWI-Prolog */
?- X is append(1,2).
ERROR: Arithmetic: `append/2' is not a function
?- X is append([1,2],[3]).
ERROR: Type error: `[]' expected, found `[1,2]' (a list)
("x" must hold one character)
I started using this test case:
test :-
between(0,1000000,N),
_ is exp(1+N/1000000),
fail.
test.
To test a new Java foreign function interface. I then
observed that SWI-Prolog stack engine causes
a little overhead:
/* SWI-Prolog, 9.1.14, optimise=false */
?- time(test).
% 2,000,001 inferences, 0.313 CPU in 0.315 seconds
(99% CPU, 6400003 Lips)
true.
/* SWI-Prolog, 9.1.14, optimise=true */
?- time(test).
% 1,000,002 inferences, 0.172 CPU in 0.176 seconds
(98% CPU, 5818193 Lips)
true.
Intrestingly GNU Prolog doesn’t use a stack engine,
just relies on the native stack. Its quite speedy without
any optimisation:
/* GNU Prolog 1.5.0 (64 bits) */
?- test.
(125 ms) yes
The internal call is tail recursive I guess, since the functor is
already checked, and a looked up handle, a function pointer,
causes the evaluation. Recently GNU Prolog has moved to GitHub,
so I can find the source code of GNU Prolog stuff more easily, things
like Load_Math_Expression. But I think the GNU Prolog approach is
only feasible, if you dare to rely on the native stack.
Concerning the new Java foreign function interface. I switched
from handles obtained by method reflection to handles that were
populated via functional interfaces. Its an itch faster, and close
to SWI-Prolog optimised, but only for JDK 8:
/* Jekejeke Prolog, 1.6.3, JDK 8, Functional Interface */
?- time(test).
% Time 171 ms, GC 2 ms, Wall 09/09/2023 22:04
true.
The above uses the native stack like GNU Prolog and no
cycle testing nothing. But I guess it burns CPU since it uses
two pointers to represent a term. I hope I can soon get rid of that.
Another brake could be the varargs array allocation.
Mild Shock schrieb am Samstag, 9. September 2023 um 22:17:13 UTC+2:
I started using this test case:
test :-
between(0,1000000,N),
_ is exp(1+N/1000000),
fail.
test.
To test a new Java foreign function interface. I then
observed that SWI-Prolog stack engine causes
a little overhead:
/* SWI-Prolog, 9.1.14, optimise=false */
?- time(test).
% 2,000,001 inferences, 0.313 CPU in 0.315 seconds
(99% CPU, 6400003 Lips)
true.
/* SWI-Prolog, 9.1.14, optimise=true */
?- time(test).
% 1,000,002 inferences, 0.172 CPU in 0.176 seconds
(98% CPU, 5818193 Lips)
true.
Intrestingly GNU Prolog doesn’t use a stack engine,
just relies on the native stack. Its quite speedy without
any optimisation:
/* GNU Prolog 1.5.0 (64 bits) */
?- test.
(125 ms) yes
Ok there is a big confusion in SWI-Prolog discourse, I was
mentioning native stack, and people think I was talking
about native code. Holy cow! Do I speak chinese, or what?
Just look for yourself. Step 1: Go to GNU Prolog GitHub,
Step 2: Lookup the C function Load_Math_Expression
Interesting find, for Dogelog Player on CPython so far:
/* Dogelog Player 1.1.1, CPython */
?- X=1+X, Y is X.
Unknown exception: 'maximum recursion depth exceeded'
What does PyPy do? What does JavaScript do? How do we
handle this exception. Are there more candidates than only (is)/2,
like for example copy_term/2, etc.. This would cover Dogelog
Player. What about formerly Jekejeke Prolog, respectively Java?
Mild Shock schrieb am Samstag, 9. September 2023 um 22:18:21 UTC+2:
The internal call is tail recursive I guess, since the functor is
already checked, and a looked up handle, a function pointer,
causes the evaluation. Recently GNU Prolog has moved to GitHub,
so I can find the source code of GNU Prolog stuff more easily, things
like Load_Math_Expression. But I think the GNU Prolog approach is
only feasible, if you dare to rely on the native stack.
Concerning the new Java foreign function interface. I switched
from handles obtained by method reflection to handles that were
populated via functional interfaces. Its an itch faster, and close
to SWI-Prolog optimised, but only for JDK 8:
/* Jekejeke Prolog, 1.6.3, JDK 8, Functional Interface */
?- time(test).
% Time 171 ms, GC 2 ms, Wall 09/09/2023 22:04
true.
The above uses the native stack like GNU Prolog and no
cycle testing nothing. But I guess it burns CPU since it uses
two pointers to represent a term. I hope I can soon get rid of that.
Another brake could be the varargs array allocation.
Mild Shock schrieb am Samstag, 9. September 2023 um 22:17:13 UTC+2:
I started using this test case:
test :-
between(0,1000000,N),
_ is exp(1+N/1000000),
fail.
test.
To test a new Java foreign function interface. I then
observed that SWI-Prolog stack engine causes
a little overhead:
/* SWI-Prolog, 9.1.14, optimise=false */
?- time(test).
% 2,000,001 inferences, 0.313 CPU in 0.315 seconds
(99% CPU, 6400003 Lips)
true.
/* SWI-Prolog, 9.1.14, optimise=true */
?- time(test).
% 1,000,002 inferences, 0.172 CPU in 0.176 seconds
(98% CPU, 5818193 Lips)
true.
Intrestingly GNU Prolog doesn’t use a stack engine,
just relies on the native stack. Its quite speedy without
any optimisation:
/* GNU Prolog 1.5.0 (64 bits) */
?- test.
(125 ms) yes
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 303 |
Nodes: | 16 (1 / 15) |
Uptime: | 12:26:59 |
Calls: | 6,797 |
Calls today: | 5 |
Files: | 12,317 |
Messages: | 5,393,922 |
Posted today: | 1 |