4 Replies Latest reply: Apr 25, 2013 9:53 AM by Paul Fowler RSS

    A Paul Fowler trick on writing Temporal Logic

    Paul Fowler
      Since I have not seen this trick specifically called out before as a pattern (or anti-pattern), I am going to try to take credit for documenting it. Minimally, I re-discovered the trick.

      This is a trick to use temporal reasoning without having TemporalOn(), TemporalBefore(), etc... throughout your document. I will warn the group that performance might suffer, but human readability of the documents is better...

      Most everyone is probably familiar with date comparisons using "the current date". In NY, we have a standard to put today = the current date in all our rulebases. At that point, you can do something like:

      [conclusion] the day is the first day of the year if
      [condition] ExtractMonth(today) = 1 and
      [condition] ExtractDay(today) = 1

      Checking for the first day of the year using normal temporal logic...
      [conclusion] the temporal day is the first day of the year if
      [condition] TemporalOn(2010-01-01, TemporalYearsSince(2010-01-01, today))

      The trick is to create a temporal variable that is a "daily calendar" and use it. Create a date attribute that changes every day betwen and including the earliest and latest dates of interest in the rulebase. In this example, the calendar day is between 2010-01-01 and the current date.
      the calendar day = AddDays(2010-01-01, TemporalDaysSince(2010-01-01, AddDays(today,1)))

      Everywhere you would use "today" (or any other date of interest) in non-temporal date comparisons or assignments, you can simply substitute "the calendar day" to make it temporal...
      [conclusion] the day is the first day of the year if
      [condition] ExtractMonth(the calendar day) = 1 and
      [condition] ExtractDay(the calendar day) = 1

      It works about everywhere and creates variables with proper change points.
      the current month = ExtractMonth(the calendar day)
      the recipient age = the number of whole years after which the calendar day is after the recipient birth date <- No TemporalYearsSince(), but still a temporal variable.
      (Note, the above is probably one of the most CPU intensive ways of finding a recipient age, as it probably compares every calendar day changepoint... The improved readability has a cost.)

      Generally, I find that it is not good to have the calendar day over too wide a time span. That said, I have also found that sometimes with complex logic, the logic is easier to understand and troubleshoot with this trick. I have also found temporal logic to be easier to explain to with this trick. Most people can grasp the concept of comparing variables to the "calendar days" as opposed to using TemporalOn(), TemporalOnOrAfter(), even though it is functionally equivalent... Of course, exclusive use of this trick may create situations with very poor performance.

      Paul Fowler
        • 1. Re: A Paul Fowler trick on writing Temporal Logic
          Sean Reardon-Oracle
          Yes - the terminology we've (or maybe it's just me) tended to use internally is "accessing the temporal timeline" and where I've used it/seen it used that attribute has been called "the temporal timeline" rather than "the calendar day".

          As you rightly point out performance can be a definite issue when using this technique and it is extremely important that the temporaldayssince be properly bounded (preferably to as narrow a range as you can).

          If you look closely you'll see that TemporalIsWeekday and TemporalOncePerMonth functions can both be mirrored using the temporal timeline. Indeed these were added as sufficient numbers of people were needing that functionality such that it made sense to avoid the need for people to explicitly declare the temporal timeline (the view being that direct functions are easier to use and that the products team at the time didn't want to "productise" the temporal timeline as an equivalent to "the current date" but for temporal functions).

          And I know you're just using it as an example but I feel I need to point out the YearStart function for "the day is the first day of the year" rather than combining with TemporalOn :-)

          (and as a best practice aside, my view is that hard-coding dates into TBR functions should be avoided on real projects).
          • 2. Re: A Paul Fowler trick on writing Temporal Logic
            Paul Fowler
            Very interesting...

            I completely agree on hard-coding dates. I was doing that in the example simply to make the example more clear.

            Also, as a precaution, I would make the calendar date uncertain on all other days not between the start and end date, so I would have used a rule table to define that variable with uncertain in the else clause. That way, if some of the rule logic accidentally compares outside the calendar, it doesn't return a certain result. Results outside that range can be misleading, since the way the rule was written above, the first value is set for all days prior to the start date.

            But, this is a method to improve rule readability. The tradeoff is readability of the text in the rules document. I am constantly striving to have rule text read and understood by policy and analyst staff, if they don't want to write the rules themselves.

            Thank you for the background.
            • 3. Re: A Paul Fowler trick on writing Temporal Logic
              Ben Rogers
              Hi Paul,

              Great post! We use this technique in Sweden to calculate a number of interesting things such as cumulative (temporal) totals.

              For example:
              Imagine if the person has a relationship to "the holiday" entity which just has "the holiday's date" as an attribute.

              the person's cumulative total of holidays (temporal) = IntervalDailySum(the start date, the day after the current date (temporal), the person's daily total of holidays (temporal))
              the person's daily total of holidays (temporal) = InstanceCountIf(the person's holidays, TemporalOn(the holiday's date))

              the day after the current date (temporal) is just your current date example (the temporal one) plus one day because the IntervalDailySum end date is exclusive.
              the start date can be something dynamic like the earliest holiday date to avoid hardcoding of dates which as Sean mentions is not a good idea.

              The rules we have are a bit more complicated (a person can take fractions of a holiday day, and can take more than one holiday on any given date for different reasons) but the cumulative totals are very useful for later on.
              For example, treating the holidays differently once the person has taken out more than 15 days... counting down the person's remaining holidays... and so on. We have a large number of instances and have found this much faster than using self-referential inferred relationships.

              What we've found is that in some cases we dont need to count each day as this is wasted processing -- we know the cumulative count won't change unless another holiday date comes along! So we have found a way of changing "the current date" to be a temporal list of "relevant dates" and summing it up that way.

              I can provide more detail if anyone is interested.
              Thanks for posting the topic, Paul!
              • 4. Re: A Paul Fowler trick on writing Temporal Logic
                Paul Fowler

                That is wild that I also solidified the technique in my own mind while dealing with holidays. I had used the technique before for some complex financial date logic, but I didn't equate the technique to readability until working with holidays.

                I wasn't counting the holidays, per se, but I had a spreadsheet with holidays in it.

                I wanted a variable that was true on every holiday derived from the spreadsheet. I didn't want to put each holiday date such as "New Years Day" surrounded by "TemporalOn(2013-01-01)".

                In the spreadsheet, I wanted two columns, one with the date "2013-01-01" in the condition column and the holiday name as a conclusion... This technique made the spreadsheet more readable to untrained eyes. Readability is key. At that point, I realized what I had been doing was a general readability pattern.