[net.lang.prolog] miscellany re income tax planning system

dave@lsuc.UUCP (David Sherman) (05/20/86)

First, thanks to everyone who responded to my plea for help
in avoiding circularity in my definition of tptype. Most people
suggested using two different predicates, one for stated facts
and one for conclusions. It turns out not to be quite so simple,
but I think that suggestion contains the seeds of the solution
I need. I'm still working on finalizing it.

Second, I've developed an interesting predicate which I call
"aggregate" which I'd like to share with the net. It comes from
the tendency of the Income Tax Act to say things like "the aggregate
of his taxable capital gains for the year". I wanted to be able to
take an arbitrary predicate which puts a number into its last
argument, call it as many times as will succeed, and total up
the numbers. Thus, if I have
	taxablecapitalgain(Taxpayer, Year, TCG)
which itself is defined in terms of more basic things (like
transactions, dispositions, proceeds, cost and so on), then I can say
	aggregate(taxablecapitalgain, fred, 1986, Aggr).
and get fred's 1986 taxable capital gains returned in Aggr.

Here's my code. I have no idea whether it will be useful to anyone,
but I'm curious as to what those more experienced with Prolog think
of it. It's probably either ingenious or stupid, but it does work.
It uses the "findall" predicate from Clocksin & Mellish chapter 7.
	
	aggregate(Goal, Arg1, Arg2, Aggr) :-
		Z =.. [Goal, Arg1, Arg2, Amount],
		findall(Amount, call(Z), List),
		listtotal(List, Aggr).

(3 other copies of "aggregate" exist, one with only Arg1,
one with Arg1, Arg2, Arg3, and one with 4 arguments for the
Goal other than the final Amount.)
	
	listtotal([], 0).
	listtotal([H|T], Total) :-
		integer(H),
		listtotal(T, Ttotal),
		Total is H + Ttotal.


Third, I'm currently wrestling with the task of generating,
for a list, every list which is a subset of that list. Thus,
for [a,b,c,d], I want findall to be able to find each of
[a,b] [a,c] [a,d] [a,b,c] [a,b,d] [a,c,d] [b,c] [b,d] [b,c,d] [c,d]. 
I've played with it for a while and can't get a handle on the
approach to take. Can anyone help? (The application is generating
every possible group of taxpayers from the list of those who
own shares in a corporation, so as to determine whether any of
them is a "related group" as defined in the Act.)

Incidentally, if anyone is interested in knowing more about my
project, I'll be happy to mail or post more. It's a comprehensive
corporate tax planning system based on the Canadian Income Tax Act.

Dave Sherman
The Law Society of Upper Canada
Osgoode Hall
Toronto, Canada  M5H 2N6
+1 416 947 3466
-- 
{ ihnp4!utzoo  pesnta  utcs  hcr  decvax!utcsri  } !lsuc!dave

bertrand@cui.UUCP (IBRAHIM Bertrand) (05/31/86)

> From: dave@lsuc.UUCP
> Subject: miscellany re income tax planning system
> ...
> Third, I'm currently wrestling with the task of generating,
> for a list, every list which is a subset of that list. Thus,
> for [a,b,c,d], I want findall to be able to find each of
> [a,b] [a,c] [a,d] [a,b,c] [a,b,d] [a,c,d] [b,c] [b,d] [b,c,d] [c,d]. 

As a first attempt to solve your problem, you could use the following
"included(X,[a,b,c,d])" predicate:

    /* included(Set,SuperSet). True if all elements of Set in SuperSet, whatever
                               the order of the elements is.
    */
    included([X|Rest],SuperSet) :-
        member(X,SuperSet),
        del(X,SuperSet,SuperRest),
        included(Rest,SuperRest).
    included([],_).

However, this predicate generates all the permutations of the possible
solutions (i.e. [a,b,c] and [a,c,b] will be generated among other solutions).
To eliminate these permutations, you can use a slightly different version of
the "del" predicate:

    /* delUpTo(Element,OriginalList,ResultingList). Deletes first elements of
       OriginalList until it finds Element, then put result in ResultingList.
    */
    delUpTo(X,[X|Rest],Rest).
    delUpTo(X,[_|ButOne],Rest) :- delUpTo(X,ButOne,Rest).

    /* included(Set,SuperSet). True if all elements of Set in SuperSet,
                               in same order. Accepts [], [X] & full set.
    */
    included([X|Rest],SuperSet) :-
        member(X,SuperSet),
        delUpTo(X,SuperSet,SuperRest),
        included(Rest,SuperRest).
    included([],_).

There is still a small problem. "included" generates some undesired solutions
(i.e. empty list, single element lists and full set). You can filter them:

    /* subset(Set,SuperSet). Like "included", but rejects [], [X] & full set.
    */
    subset(Set,SuperSet) :-
        included(Set,SuperSet),
        filtered(Set,SuperSet).

    filtered(  []   ,   _   ) :- !,fail.
    filtered(  [_]  ,   _   ) :- !,fail.
    filtered(FullSet,FullSet) :- !,fail.
    filtered(   _   ,   _   ).

    included([X|Rest],SuperSet) :-
        member(X,SuperSet),
        delUpTo(X,SuperSet,SuperRest),
        included(Rest,SuperRest).
    included([],_).

    delUpTo(X,[X|Rest],Rest).
    delUpTo(X,[_|ButOne],Rest) :- delUpTo(X,ButOne,Rest).

As you mentioned, you can use the "findall" predicate to generate a list of
all solutions:

    findall(X,subset(X,[a,b,c,d]),ListOfSolutions)

Hope this helps.

B. Ibrahim	IBRAHIM@CGEUGE51.BITNET
		cernvax!cui!bertrand.uucp
		cui!bertrand@cernvax.UUCP
		bertrand@cui.unige.chunet

rjmh@glasgow.glasgow.UUCP (Prof. John Hughes) (06/02/86)

>> From: dave@lsuc.UUCP
>> Subject: miscellany re income tax planning system
>> ...
>> Third, I'm currently wrestling with the task of generating,
>> for a list, every list which is a subset of that list. Thus,
>> for [a,b,c,d], I want findall to be able to find each of
>> [a,b] [a,c] [a,d] [a,b,c] [a,b,d] [a,c,d] [b,c] [b,d] [b,c,d] [c,d]. 
>

This should do the trick:

included(Subset,Set) is true if Subset is a subset of Set

included([],Set).
included([X|Subset],Set):-append(_,[X|Rest],Set),included(Subset,Rest).

It only includes subsets whose elements are in the same order as in the
original list.