Объединение чистых предикатов

Я пытаюсь объединить некоторые чистые предикаты из предыдущих вопросов о переполнении стека, чтобы создать свой собственный предикат.

Я хочу дать список c (с которыми связаны факты - "at") и термин "особенность", в котором есть оператор и порог для "at". Я хочу разделить списки c, если у c нет соответствующего 'at' из 'feature', он перейдет в ложный раздел, в противном случае оператор проверит 'at' для этого 'c' и разделит с соответственно.

Например:

?-cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).

Должно привести к:

Ts = [c3], %c3 has an at2 >= 10
Fs = [c1,c2]. %c1 has at2 <10 and c2 does not have an at2

Вот код, который у меня есть:

:-use_module(library(clpfd)).

cpgpartition_ts_fs_feature([],[],[],_).
cpgpartition_ts_fs_feature([X|Xs0],Ts,Fs,Feature):-
    Feature = feature(At,_,Op,FValue),
    cpg_ats_i(X,AtList),
    atom_concat(#,Op,Op2), %make clpfd operator
    Test =..[Op2,AtValue3,FValue],
    if_(memberd_t(attribute(At,AtValue3),AtList),
       (
       if_(call(Test), (Ts=[X|Ts0],Fs=Fs0),
       (   Ts =Ts0,Fs=[X|Fs0]))
       )
       ,Fs=[X|Fs0]),
    cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature).

if_(If_1, Then_0, Else_0) :-
   call(If_1, T),
   (  T == true -> call(Then_0)
   ;  T == false -> call(Else_0)
   ;  nonvar(T) -> throw(error(type_error(boolean,T),_))
   ;  /* var(T) */ throw(error(instantiation_error,_))
   ).

bool01_t(1,true).
bool01_t(0,false).

=(X, Y, T) :-
   (  X == Y -> T = true
   ;  X \= Y -> T = false
   ;  T = true, X = Y
   ;  T = false,
      dif(X, Y)                             % ISO extension
      % throw(error(instantiation_error,_)) % ISO strict
   ).

#=<(X,Y,Truth) :- X #=< Y #<==> B, bool01_t(B,Truth).

#<( X,Y,Truth) :- X #<  Y #<==> B, bool01_t(B,Truth).

#>( X,Y,Truth) :- X #>  Y #<==> B, bool01_t(B,Truth).

#>=(X,Y,Truth) :- X #>= Y #<==> B, bool01_t(B,Truth).

list_memberd_t([]    ,_,false).
list_memberd_t([Y|Ys],X,Truth) :-
   if_(X=Y, Truth=true, list_memberd_t(Ys,X,Truth)).

list_memberd_truth(Xs,X,Truth) :- list_memberd_t(Xs,X,Truth).

memberd_t(X,Xs,Truth) :- list_memberd_t(Xs,X,Truth).

value_intvalue(attribute(_A,X),attribute(_A,Y)):-
        AtValue2 is X *100, %Convert decimal number to integer.
        Y is integer(AtValue2).

cpg_ats_i(C,AtList):-
        cpg_ats(C,Ats),
        maplist(value_intvalue,Ats,AtList).

cpg_ats(c1,[attribute(at1,0.5),attribute(at2,0.03)]).
cpg_ats(c2,[attribute(at1,0.02)]).
cpg_ats(c3,[attribute(at2,0.1),attribute(at3,0.04),attribute(at4,0.08)]).

При попытке тестового запроса я получаю:

cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Fs = [c1, c2] ;
Fs = [c1, c2, c3] ;
Fs = [c1, c2] ;
Fs = [c1, c2, c3].

И что интересно, результаты меняются, если порядок записи отличается.

?- cpgpartition_ts_fs_feature([c3,c1,c2],Ts,Fs,feature(at2,_,>=,10)).
Ts = [c3|_12950],
Fs = [c1, c2] ;
Ts = [c3|_12950],
Fs = [c1, c2] ;
Fs = [c3, c1, c2] ;
Fs = [c3, c1, c2].

Я думаю, что это потому, что следующий запрос возвращает результаты с dif/2 ограничения, которые кажутся неподходящими для того, что я пытаюсь сделать, мне нужны только конкретные решения.

    ?- cpg_ats_i(C,Ats),   if_(memberd_t(attribute(at2,AtValue),Ats),Q=true,Q=false).
C = c1,
Ats = [attribute(at1, 50), attribute(at2, 3)],
AtValue = 3,
Q = true ;
C = c1,
Ats = [attribute(at1, 50), attribute(at2, 3)],
Q = false,
dif(AtValue, 3) ;
C = c2,
Ats = [attribute(at1, 2)],
Q = false ;
C = c3,
Ats = [attribute(at2, 10), attribute(at3, 4), attribute(at4, 8)],
AtValue = 10,
Q = true ;
C = c3,
Ats = [attribute(at2, 10), attribute(at3, 4), attribute(at4, 8)],
Q = false,
dif(AtValue, 10).

Также цель состоит в том, чтобы этот код выполнялся на большом наборе данных, список c будет иметь длину в сотни тысяч, и каждый c может иметь 50 тыс. Ат, как я могу решить требования к памяти? и может ли другой подход использовать нечистые предикаты занимать меньше памяти?

2 ответа

Решение

Как вы упомянули, проблема в строке dif(X,Y) в определении:

=(X, Y, T) :-
   (  X == Y -> T = true
   ;  X \= Y -> T = false
   ;  T = true, X = Y
   ;  T = false,
      dif(X, Y)                             % ISO extension
      % throw(error(instantiation_error,_)) % ISO strict
   ).

это потому что если вы попробуете:

memberd_t(attribute(at2,X),[attribute(at1,0.5),attribute(at2,0.03)],T).
X = 0.03,
T = true ;
T = false,
dif(X, 0.03).

Вот точка выбора, которая дает решение: T = false,dif(X, 0.03). приведет к выполнению части Fs=[X|Fs0] из:

if_(memberd_t(attribute(At,AtValue3),AtList),
       (
       if_(call(Test), (Ts=[X|Ts0],Fs=Fs0),
       (   Ts =Ts0,Fs=[X|Fs0]))
       )
       ,Fs=[X|Fs0]),

Кроме того, это неправильный ответ, так как если у вас есть атрибут (at2,0.03) в Atlist вы ожидаете memberd_t возвращать X = 0.03, T = true который вызовет Then_0 часть if_/3 (и нет другого решения с T = false, которое приведет к другим точкам выбора, выполняющим часть Else_0).

Так что вы можете удалить T = false,dif(X, Y) из =/3 а теперь давайте попробуем:

?- cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Fs = [c1, c2].

хорошо, но где Ц?

Итак, есть еще одна ошибка:

Вышесказанное говорит, что это успешно для Fs = [c1,c2] и за каждый ц. Это потому, что выполнение Else_0 часть if_/3 который выполняет Fs список, который вы не ограничиваете Ts список просто оставить как Ts а позже позвони cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,Feature) с другим Ts0 список независимых от Ц. Итак, добавьте:

if_(memberd_t(attribute(At,AtValue3),AtList),
       (
        if_(call(Test), (Ts=[X|Ts0],Fs=Fs0), (Ts =Ts0,Fs=[X|Fs0]))
       )
       ,(Fs=[X|Fs0], Ts = Ts0 )),
                     ^^^^^^^^
                     here added 

Наконец-то я в соответствии с рекомендациями @false лучше заменить Test =..[Op2,AtValue3,FValue], ..., call(Test) от call(Op2,AtValue3,FValue) поскольку call/N является частью ISO и вписывается в оригинальную систему типов Mycroft O'Keefe.

Теперь давайте попробуем еще раз:

?- cpgpartition_ts_fs_feature([c1,c2,c3],Ts,Fs,feature(at2,_,>=,10)).
Ts = [c3],
Fs = [c1, c2].

Кажется правильным и детерминированным:)!!

Что касается части памяти вашего вопроса, я не уверен, но предпочитаю детерминированные предикаты, которые не оставляют точек выбора для эффективности памяти. Использование чистых предикатов сделает вашу программу более реляционной и будет вести себя лучше, но я не уверен, что if_/3 так эффективно использует память, так как содержит много вызовов, но я не уверен, что кто-то другой мог бы ответить на эту часть более четко.

Благодаря ответу от Coder я придумал:

cpgpartition_ts_fs_feature([],[],[],_).
cpgpartition_ts_fs_feature([X|Xs0],Ts,Fs,feature(At,_,Op,FValue)):-
    cpg_ats_i(X,AtList),
    atom_concat(#,Op,Op2), %make clpfd operator
    maplist(atterm_atname,AtList,Ats),
    if_(memberd_t(At,Ats),
      (
      memberchk(attribute(At,AtValue3),AtList),
      if_(call(Op2,AtValue3,FValue), (Ts=[X|Ts0],Fs=Fs0),
        (   Ts =Ts0,Fs=[X|Fs0]))
      ),
      (Fs=[X|Fs0],Ts=Ts0)
    ),
    cpgpartition_ts_fs_feature(Xs0,Ts0,Fs0,feature(At,_,Op,FValue)).


atterm_atname(attribute(At,_),At).

Что позволило мне получить тот же результат без изменения определения =/3,

Нынешняя предлагаемая реализация if_/3 является неудачной, так как она ставит точку выбора на reification, а не на if-then-else itsef. Вот пример недостатка:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)

?- call(','(X=Y,2=3),B).
X = Y,
B = false ;  %%% a bloody choice point %%%
B = false,
dif(X, Y). 

Здесь мы видим гораздо лучший интеллект для соединения, например, #/\ от CLP(FD) в SWI-Prolog. Точка выбора не создается:

Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)

?- X #= Y #/\ 2 #= 3 #<==> B.
B = 0,
X in inf..sup,
Y in inf..sup. 

В настоящее время я работаю над лучшей if_/3, которая включает в себя этот вид интеллекта в своей работе. Базовым шаблоном для улучшения if_/3 будет:

if(Cond, Then, Else) :-
   reify(Cond, Bool),
   thenelse(Bool, Then, Else)

thenelse(1, Then, _) :- Then.
thenelse(0, _, Else) :- Else. 

Идея состоит в том, чтобы не ставить точки выбора в reify/2, избегать их как можно дольше. В настоящее время (=)/3 создает точку выбора, что плохо при объединении

condinition. Может быть, мы также можем организовать одинаковые условия в разных местах кода, совместно использовать одну и ту же переменную логического индикатора. Работаю над этим...

Другие вопросы по тегам