clpz icon indicating copy to clipboard operation
clpz copied to clipboard

min could settle if one input and the other's upper bound are known

Open jeshan opened this issue 11 months ago • 1 comments

I'm wondering if we could we unify B=A here:

A in 0..100, B #= min(100, A).

returns:

clpz:(B#=min(100,A)),
clpz:(A#>=B),
clpz:(A in 0..100),
clpz:(B in 0..100) ? 
yes

Especially if A can definitely not be less than 100:

| ?- [A, B] ins 0..100, B #= min(100, A), B#\=A, labeling([], [A,B]).
no

jeshan avatar Jan 18 '25 19:01 jeshan

it's possible to make the constraint stronger:

propagator for `min`
% Z=min(X, Y)
% min(X, Y) = min(Y, X), X =< Y <=> X = min(X, Y)
%
% X =< Y => Z = X
% Z = X => Z =< Y
% Z < X => Z = Y
run_propagator(pmin(X,Y,Z), MState) -->
        (   nonvar(X), nonvar(Y), nonvar(Z)
        ->  Z =:= min(X, Y)
        ;   X == Y
        ->  kill(MState),
            queue_goal(Z = X)
        ;   run_propagator(pmin2(X,Y,Z), MState),
            run_propagator(pmin2(Y,X,Z), MState),
            run_propagator(pmin1(X,Y,Z), MState),
            run_propagator(pmin1(Y,X,Z), MState),
            run_propagator(pmin0(X,Y,Z), MState),
            run_propagator(pmin0(Y,X,Z), MState)
        ).

run_propagator(pmin2(X,Y,Z), MState) -->
        (   nonvar(X), nonvar(Y), nonvar(Z)
        ->  true
        ;   (   nonvar(X), nonvar(Y), X =< Y
            ->  kill(MState),
                queue_goal(Z = X)
            ;   true
            ),
            (   nonvar(Z), nonvar(X)
            ->  (   Z = X
                ->  kill(MState),
                    % queue_goal(#Z #=< #Y)
                    { fd_get(Y, YD0, YPs0),
                      domain_remove_smaller_than(YD0, Z, YD1) },
                    fd_put(Y, YD1, YPs0)
                ;   Z < X
                ->  kill(MState),
                    queue_goal(Z = Y)
                ;   false % Z > X
                )
            ;   true
            )
        ).

run_propagator(pmin1(X,Y,Z), MState) -->
        (   nonvar(X), nonvar(Y)
        ->  true
        ;   nonvar(Y), nonvar(Z)
        ->  true
        ;   nonvar(Z), nonvar(X)
        ->  true
        ;   (   { nonvar(X), fd_get(Y, _, n(YL), _, _), X =< YL }
            ->  kill(MState),
                queue_goal(Z = X)
            ;   { fd_get(X, _, _, n(XU), _), nonvar(Y), XU =< Y }
            ->  kill(MState),
                queue_goal(Z = X)
            ;   true
            ),
            (   Z == X, nonvar(Y)
            ->  kill(MState),
                % queue_goal(#Z #=< #Y)
                { fd_get(Z, ZD0, ZPs0),
                  domain_remove_greater_than(ZD0, Y, ZD1) },
                fd_put(Z, ZD1, ZPs0)
            ;   { nonvar(Z), fd_get(X, _, n(XL), _, _), Z < XL }
            ->  kill(MState),
                queue_goal(Z = Y)
            ;   { fd_get(Z, _, _, n(ZU), _), nonvar(X), ZU < X }
            ->  kill(MState),
                queue_goal(Z = Y)
            % ;   nonvar(Z)
            % ->  true % queue_goal((#Z #=< #X, #Z #=< #Y))
            ;   true
            ),
            (   { fd_get(X, _, n(XL), _, _), nonvar(Y), XL =< Y }
            ->  % queue_goal(#Z #>= #XL)
                { fd_get(Z, ZD2, ZPs1),
                  domain_remove_smaller_than(ZD2, XL, ZD3) },
                fd_put(Z, ZD3, ZPs1)
            ;   true
            )
        ).

run_propagator(pmin0(X,Y,Z), MState) -->
        (   nonvar(X)
        ->  true
        ;   nonvar(Y)
        ->  true
        ;   nonvar(Z)
        ->  true
        ;   (   { fd_get(X, _, _, n(XU), _), fd_get(Y, _, n(YL), _, _), XU =< YL }
            ->  kill(MState),
                queue_goal(Z = X)
            ;   true
            ),
            (   Z == X
            ->  kill(MState),
                queue_goal(#Z #=< #Y)
            ;   { fd_get(Z, _, _, n(ZU), _), fd_get(X, _, n(XL), _, _), ZU < XL }
            ->  kill(MState),
                queue_goal(Z = Y)
            ;   true
            ),
            (   { fd_get(X, _, n(XL), _, _), fd_get(Y, _, n(YL), _, _), XL =< YL }
            ->  % queue_goal(#Z #>= #XL)
                { fd_get(Z, ZD2, ZPs1),
                  domain_remove_smaller_than(ZD2, XL, ZD3) },
                fd_put(Z, ZD3, ZPs1)
            ;   true
            )
        ).

This version is not too efficient. It still needs some testing. This version of min could land on Scryer Prolog. The maximum would also have a stronger version.

notoria avatar Feb 03 '25 16:02 notoria