5 ms·
When I tried to use SWI prolog for a toy-task, it failed miserably when dealing with facts that contained numeric expressions. I think if a variant of Prolog wa
by hendzen 9y ago
When I tried to use SWI prolog for a toy-task, it failed miserably when dealing with facts that contained numeric expressions. I think if a variant of Prolog was paired with an SMT solver (e.g. Z3) it would be much more relevant today.
For example, when dealing with bitemporal data (common in finance) you might have a set of facts with two date range attributes. Lets simplify by saying we have a set of facts each having a start date and end date. Here is some non-working Prolog that could work if there was such a capability.
entity('TimeWarner').
ticker(entity('TimeWarner'), 'TWC', date(1999-01-01), date(2014-04-31)).
ticker(entity('TimeWarner'), 'AOL', date(2014-05-01), date(9999-01-01)).
current_at(ticker(entity(_), _, Start, End), T) :-
T @> Start,
End @> T.
-- find current ticker for Time Warner
current_at(ticker(entity('TimeWarner'), _, _), date(2017-05-29))
-- SWI prolog can not unify the above clause!
(The code above is semi-pseudocode - but I did try and fail to make this work some time ago)
Now, it turns out that this sort of exists already, it's called Answer Set programming. There is one implementation out there [0] - but I didn't feel like dredging up an old research project.
[0] - http://potassco.sourceforge.net/ http://potassco.sourceforge.net/
- 75dvtwin 9y agothe above seems to have only one temporal dimention, not 2. Why did you think the above is a bi-temporal data?
- hendzen 9y agoI was simplifying the example.
- YeGoblynQueenne 9y agoWould something like this work for you? (Edit: it parse_time/2 is a Swi-Prolog built-in so this won't run in other Prologs. chielk's solution above is best) entity('TimeWarner'). ticker(entity('TimeWarner'), 'TWC', date('1999-01-01'), date('2014-04-31')). ticker(entity('TimeWarner'), 'AOL', date('2014-05-01'), date('9999-01-01')). current_at(Entity,TLC,Date_start,Date_end,date(T)):- entity(Entity) ,ticker(entity(Entity),TLC,date(Date_start),date(Date_end)) ,parse_time(Date_start,DS) ,parse_time(Date_end,DE) ,parse_time(T,Tp) ,Tp > DS ,DE > Tp. ?- current_at('TimeWarner', TLC, DateStart, DateEnd, date('2017-05-29')). TLC = 'AOL', DateStart = '2014-05-01', DateEnd = '9999-01-01'. Basically, you need to treat the date as an atom and it's better to avoid wrapping up everything in compounds like you're doing in ticker(entity('TimeWarner') etc. That just makes it harder to read and write your queries. You can just pass the argument values in the head of the rule and match them with the facts in the database like I do above. If you really require the dates to be in yyyy-mm-dd format, the code above becomes slightly more verbose but it's not the end of the world.
- chielk 9y agoI simplified your notation a bit and added some slightly silly predicates, but this works: ticker('TimeWarner', 'TWC', date(1999-01-01), date(2014-04-31)). ticker('TimeWarner', 'AOL', date(2014-05-01), date(9999-01-01)). transform_date(date(Y-M-D), R) :- R is Y * 416 + M * 32 + D. after(Adate, Bdate) :- transform_date(Adate, A), transform_date(Bdate, B), A @> B. current_at(Name, C, Start, End, T) :- ticker(Name, C, Start, End), after(T, Start), after(End, T). ?- current_at('TimeWarner', C, S, E, date(2017-05-29)). C = 'AOL', S = date(2014-5-1), E = date(9999-1-1).
- hendzen 9y agoThanks - this is great. I stand corrected!
- jessaustin 9y agoticker(entity('TimeWarner'), 'TWC', date(1999-01-01), date(2014-04-31)). ticker(entity('TimeWarner'), 'AOL', date(2014-05-01), date(9999-01-01)). Slightly OT, but half-open intervals are just better for this sort of thing. After all, the change date could have been '2014-05-01T10:05:23.3982Z' instead of '2014-05-01'. [EDIT:] In case it wasn't clear, I'm suggesting that it would be better to write this: ticker(entity('TimeWarner'), 'TWC', date(1999-01-01), date(2014-05-01)). ticker(entity('TimeWarner'), 'AOL', date(2014-05-01), date(9999-01-01)).
- tom_mellior 9y agochielk's solution is good, but it doesn't have to be as complex. You were on the right track with using @> (the "standard order of terms"), which does the right thing automatically. Here's your fixed definition: current_at(Entity, C, Start, End, Current) :- ticker(Entity, C, Start, End), Start @< Current, Current @< End. What you got wrong was the fact that you need to call ticker/4 in the body of your predicate instead of expecting such a term as an argument. This works with the database you posted: ?- current_at(entity('TimeWarner'), C, S, E, date(2017-05-29)). C = 'AOL', S = date(2014-5-1), E = date(9999-1-1).
- YeGoblynQueenne 9y agoThat's a nice, short answer but t's a bit unsafe, isn't it? The standard order of terms doesn't know anything about dates. The above works because compounds are compared recursively on their arguments. For those less familiar with Prolog, in Prolog terms, a date in a format like 2014-5-1 is a compound term, a predicate -/2 with the operator "-" as a functor and 2 arguments: 2014-5 and 1. The first of those is, again, a compound, also with functor - and two arguments, 2014 and 5. So the entire date is a recursive term. A comparison predicate, like @>/2, etc, will walk over the arguments of this term and compare them as it goes - but since a "date" is not a Prolog type (only "number" and "atom" really are) it will not treat a term meant as a date in any special manner. Which, in the end, means that the following queries are all true: ?- 2014-5-1 @< 9999-1-1. true. ?- 2014-5-0 @< 9999-1-1. true. ?- 2014-5 @< 9999-1-1. true. ?- 2014-50-1 @< 9999-1-1. true. The third query has some merit, but the rest will need some type-checking somewhere above the comparison line, to make sure you're processing dates and not just arbitrary numbers. In that sense, wrapping up dates in a date/1 term, as the OP did, may not be such a bad idea after all - or at the very least one could write a is_date/1 predicate to handle type checking: ?- is_date(2014-5-0). false. ?- is_date(2014-5-1). true. But er, that's left as an exercise to the reader :)
- tom_mellior 9y agoYou're right about the safety, though without a formal specification I think it was just good enough for an HN comment :-)