1

For my program the Knowledge Base has atomic statements and rules such as

store(itemName, ProductType)

ex: store(iPhone5, phone)

manufacturer(itemName, Company)

ex: manufacturer(iPhone5, apple)

query is to find out if there is a company that manufacturered more than 2 distinct product types, so for apple there would be Macbook, iPad, and iPhone

Here is our query:

?- store(ItemID1, ProductType1), manufacturer(ItemID1, Company), store(ItemID2, ProductType2), manufacturer(ItemID2, Company), store(ItemID3, ProductType3), manufacturer(ItemID3, Company), not ProductType1 = ProductType2, not ProductType1 = ProductType3, not ProductType2 = ProductType3.

It gives the correct answer listing 3 apple products but pressing more gives the same answer again and again. Is there a way to stop this?

Note: I am aware of the other answers to other questions given on this site but don't know how to implement them given my limited knowledge of Prolog and not sure if those answers apply to this question.

Note: For this question I am not allowed to use ! -> and ; operators

false
  • 10,264
  • 13
  • 101
  • 209
emanyalpsid
  • 329
  • 1
  • 5
  • 19

2 Answers2

1

Discounting performance issues, your core problem seems to be that you look for distinct product types, but have variables for the products themselves. So for example for hypothetical distinct product types T1, T2, T3, and products P1a P1b P1c of type T1, P2 of type T2 and P3 of type T3, you'd get solutions for (P1a,P2,P3), (P1b,P2,P3) and (P1C,P2,P3), which are all equivalent from your point of view.

To avoid that you'd need to fuse the (type,product) pairs to a single per type.

PS: you'd get better answers if you trimmed down your code to the minimum that exposes the problem (for example, that price predicate seems unnecessary) and provided us with the actual output you have.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
JB.
  • 40,344
  • 12
  • 79
  • 106
  • I've changed your terminology to that used in the question. And your solution doesn't look right to me: e.g. the KB `store(a,phone). store(b,phone). manufacturer(a,nokia). manufacturer(b,apple).` (and two more Apple products in the store): it is essential both `a` and `b` are present, for `apple` solution to be found. – Will Ness Sep 24 '13 at 07:08
  • @WillNess I don't quite see how the `a` phone (the `nokia` one) is relevant. OP's query very explicitly has a single `Company` variable. In the absence of cuts, the solution set is independent of the conjuncts' order, hence in plain English the set of (I1,I2,I3,P1,P2,P3,C) tuples with P1, P2, P3 distinct. Sources of multiplicity wrt {P1,P2,P3,C} sets (not tuples) are the inclusions I_i∈P_i (my answer) and set/tuple ordering (Daniel Lyons's answer). – JB. Sep 24 '13 at 07:29
  • I read your suggestion as though `(a,phone)` and `(b,phone)` must be made one - "single per type".... IOW I don't quite get what you meant by that. – Will Ness Sep 24 '13 at 07:46
  • My (intended) suggestion was that `store(iphone4,phone),manufacturer(iphone4,apple)` and `store(iphone5,phone),manufacturer(iphone5,apple)` must be made one. – JB. Sep 24 '13 at 07:59
  • I've rolled back. I shouldn't have edited since I didn't understand your answer. :) – Will Ness Sep 24 '13 at 08:01
1

I think there are a few other ways to solve it in addition to JB's recommendation. One cheesy trick is to enforce an ordering over ProductTypeN by adding this to the end of your query:

ProductType1 @> ProductType2, 
ProductType2 @> ProductType3.

This will get you one permutation of the three product types, eliminating the combinatorial explosion you're seeing.

A more sophisticated technique would be to use setof/3 to enumerate solutions. Because it produces all the answers as a set, it must sort the values, which removes duplicates in basically the same fashion as the cheesy trick.

more_than_two_product_types(Manufacturer) :-
    setof(Manufacturer, T^manufacturer(T,Manufacturer), Manufacturers),
    member(Manufacturer, Manufacturers),
    setof(Type, Thing^(manufacturer(Thing, Manufacturer), store(Thing, Type)), [_,_,_|_]).

This is pretty complex so let's break it down. First, this condition produces a list of manufacturers:

setof(Manufacturer, T^manufacturer(T,Manufacturer), Manufacturers),

The setof/3 meta-predicate takes a constructor expression, a template expression, and returns a list of results. This one is going to gather up all the solutions for manufacturer(T, Manufacturer) and collect them into a list. We're only interested in the name of the manufacturer, so the template argument is just Manufacturer. The T^ syntax tells setof/3 that T is a free variable in manufacturer(T, Manufacturer), so we don't care what it instantiates to. This is essential, or setof/3 itself will produce one solution for each type, which isn't what we want.

This line iterates the new list of manufacturers:

member(Manufacturer, Manufacturers),

This complex line finds us all the types of products made by a manufacturer:

setof(Type, Thing^(manufacturer(Thing, Manufacturer), store(Thing, Type)), [_,_,_|_]).

The goal expression here is the sequence (manufacturer(Thing, Manufacturer), store(Thing, Type)). This says, find a Thing made by this manufacturer and then find the Type for that thing. Again, the Thing^ syntax says we don't really care what the things are, so get all the Type solutions at once. Instead of binding this to a list for us to process, the template [_,_,_|_] unifies with any list with at least three items. We don't really care what those items are, so they're all blank. Test in your console and see what it unifies with:

?- [1,2,3] = [_,_,_|_].
true.

?- [1,2] = [_,_,_|_].
false.

?- [1,2,3,4] = [_,_,_|_].
true.

This is going to generate at least three solutions and then throw them away to succeed, or fail if it generates fewer.

As you can see, there's more than one way to skin a cat with Prolog. :)

Daniel Lyons
  • 22,421
  • 2
  • 50
  • 77
  • your first suggestion, `moreThanTwo(C):- maplist(store,[I,J,K],[T,R,S]),maplist(manufacturer,[I,J,K],[C,C,C]), T@>R,R@>S` could still produce multiples for same `C`. But it *is* nice; and easy to throw into a `setof`. – Will Ness Sep 24 '13 at 07:15
  • @WillNess Yeah - I suppose if the manufacturer had 4 or more product types. I should have made a more comprehensive test database. – Daniel Lyons Sep 24 '13 at 13:45