Discussions related to Visual Prolog
Kari Rastas
Active Member
Posts: 36
Joined: 4 Mar 2000 0:01

Unreachable or excessive code?

Unread post by Kari Rastas »

I have a problem with code in counting quarter figures in a timeseries data.

I need to count the data between the years 2002 to recent "times". Let's imagine the last known quarter of 2014 would be quarter number 2. So then I could count Q1 and Q2 of 2014 and checkQuarter(2014,"Q3") and checkQuarter(2014,"Q4") would produce false() with the code below.

The code below produces numerous
w651 Unreachable or excessive code (removed by the optimization)

and I really can not figure why.

Code: Select all

constants   lastKnownYearInQuarters : unsigned = 2014.   lastKnownQuarter : string = "Q2".   predicates     checkQuarter:(unsigned Year, string Quarter) -> boolean.   clauses     checkQuarter(Year,_) = true():-         Year < lastKnownYearInQuarters,!.       checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         lastKnownQuarter = "Q4",         list::isMember(Q,["Q1","Q2","Q3","Q4"]),!,     checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         lastKnownQuarter = "Q3",         list::isMember(Q,["Q1","Q2","Q3"]),!.     checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         lastKnownQuarter = "Q2",         list::isMember(Q,["Q1","Q2"]),!.     checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         lastKnownQuarter = "Q1",         list::isMember(Q,["Q1"]),!.       checkQuarter(_Year,_Q) = false().  
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

Since lastKnownQuarter is a constant ("Q2"), clauses containing a statement like this

Code: Select all

lastKnownQuarter = "Q4",
must always fail, so the test for list membership in the flagged clauses can never be reached.
B.Hooijenga
VIP Member
Posts: 57
Joined: 11 Jul 2002 23:01

Unread post by B.Hooijenga »

Hello Kari,

I could not resist looking for a solution.

Code: Select all

predicates     makemonths:(string*,string*) -> string*. clauses     makeMonths([lastKnownQuarter|_] ,T) = [lastKnownQuarter|T] :-!.     makeMonths([H|R],T) = makemonths(R,[H|T]) :-!.     makemonths(_,_) = _ :-         exception::raise_error().   predicates      checkQuarter:(unsigned Year, string Quarter,string* Months) -> boolean. clauses     checkQuarter(Year,_,_) = true():-         Year < lastKnownYearInQuarters,!.     checkQuarter(Year,Q,Months) = true():-            Year = lastKnownYearInQuarters,            list::isMember(Q,Months),!.     checkQuarter(_,_,_) = false().

You can test it with for instance

Code: Select all

clauses test() :-    Months = makeMonths(["Q1","Q2","Q3","Q4"],[]),    A = checkQuarter(2014,"Q1",Months),    stdio::write(A,"\n").
Kind regards
Ben
Kari Rastas
Active Member
Posts: 36
Joined: 4 Mar 2000 0:01

Unread post by Kari Rastas »

Form the point of the clauses logic correctness the value of what is written above in the constant segment should not have any "effect" to compiling the classes. This constants are changed during time, for example lastKnownQuarter every 3 months. The code of the predicate not.

The logic of clauses is that if
1) the checked Year is same as the last know (Year = lastKnownYearInQuarters)
2) if the lastKnownQuarter is for example "Q2" (lastKnownQuarter="Q2")
3) then "Q1" and "O2" are accepted values for Q (list::isMember(...)).

In my original code I have constants like these lastKnownYearInQuarters and lastQuarter etc numerous other constants in a separated different class. Then I have to correct only to one place the new value of last year, quarter symbol etc numerous other limits in many classes.
Kari Rastas
Active Member
Posts: 36
Joined: 4 Mar 2000 0:01

Unread post by Kari Rastas »

Thanks Ben it works. However a bit "cryptic" solution from the point of simplicity and pure logic. :shock:

I have monthly data (Finnish Customs data, updated every month), which I then process to quarter and year levels.
See http://rastka.com/importexport1.html

I count the quarter values for groups of countries (like EU, Africa, BRICS-countries) is style

Code: Select all

foreach YEAR= std::fromTo(2002,lastKnownYear) do    foreach Q = list::getMember(["Q1","Q2","Q3","Q4"]) do        if checkQuarter(YEAR,Q) = true() then             doCounting ....        end if    .......
So I needed a predicate which stops the counting to the lastKnownQuarter in the last known year. Before I did the counting to all quarters of the last know year and then did delete the values of the facts of those quarters which did not yet exist.
User avatar
George
Active Member
Posts: 47
Joined: 19 Sep 2011 8:54

Unread post by George »

:idea: :idea: :idea:

Code: Select all

facts     lastKnownYearInQuarters : unsigned := 2014.     lastKnownQuarter : string := "Q2".   predicates         addTillLastQuater : (). clauses         addTillLastQuater():-             QuaterList = ["Q1", "Q2", "Q3", "Q4"],             Year = varM_unsigned::new(2012),             lastKnownQuarter := "Q2",             ExistLoop = varM_Boolean::new(false ),             %Loop through 2012/2013 and 2014 upto Q2             foreach std::repeat() and                         if ExistLoop:value = true then                             !,                             fail                         end if             do                     if false = addAllData(Year:value, QuaterList) then                         ExistLoop:value := true                     end if,                     Year:value := Year:value + 1             end foreach.   predicates     addAllData : (unsigned, string*) -> boolean.   clauses     addAllData(_, []) = true:- !.     addAllData(Year, [HQuater|RestQuater]) = Result:-             if true = checkIsValidQuater(Year, HQuater) then                 %You have to add all your record for the current quater here.. Getthe value for the current year and quater and do the calulation..                 Result = addAllData(Year, RestQuater)             else                 Result = false             end if.   predicates     checkIsValidQuater : (unsigned, string Quater) -> boolean.   clauses     checkIsValidQuater(Year, Quater) = Results:-         if Year < lastKnownYearInQuarters then             Results = true()         elseif Year = lastKnownYearInQuarters and lastKnownQuarter >= Quater then             Results = true()         else             Results = false()         end if.       predicates     onPushButton2Click : button::clickResponder. clauses     onPushButton2Click(_Source) = button::defaultAction:-         addTillLastQuater().
Kind Regards,
George Ananth. S | Prolog Developer
georgeananth.prolog@gmail.com
+91 9791499282
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Re:

Unread post by Harrison Pratt »

Three thoughts on this:

(1) If you reverse the order of the statements so the that mandatory fail clause (or the given constant) is last, the compiler does not optimize away any code:

Code: Select all

    checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         list::isMember(Q,["Q1","Q2","Q3","Q4"]),         lastKnownQuarter = "Q4".     % <== MAKE THIS TEST LAST to avoid unreachable code warning     checkQuarter(Year,Q) = true():-         Year = lastKnownYearInQuarters,         lastKnownQuarter = "Q3",         list::isMember(Q,["Q1","Q2","Q3"]),!.
(2) What you are doing with your original code is, in effect, Conditional Compilation (like the VIP 5.2 ifdef / elsedef /enddef directives). So, it doesn't really matter that the executable code is optimized away since it won't ever be reached ... until you change the constant and recompile the class, as you plan to do.

(3) If you use a static fact for lastKnownQuarter, the compiler will know that the code that follows the test for "Q4" might change in the future and will not optimize away the code following the test.
Harrison Pratt wrote:Since lastKnownQuarter is a constant ("Q2"), clauses containing a statement like this

Code: Select all

lastKnownQuarter = "Q4",
must always fail, so the test for list membership in the flagged clauses can never be reached.
Kari Rastas
Active Member
Posts: 36
Joined: 4 Mar 2000 0:01

Unread post by Kari Rastas »

I tried already before posting this question different order with clauses. The unreachable code warning comes always to those quarter clauses which are "outside" the current string constant. So if the constant value is "Q3" the warning comes with clauses where is used "Q1", "Q2" and "Q4". Etc. I also tried to change the order of inside the clause (it had rather strange results with there warnings).

Well one can call this using a constant in style I do as conditional compilation. VIP has in PFC numerous constants, but is using them also that conditional compilation? I have had no problems (warnings) with using numeric constants, only with these string (quarter period number) constants. I understand, that the clauses where constant value is "outside" the used constant value are useless and unnecessary. So the warning is not wrong, but it is a bit worrying and confusing.

The problem with facts is that "passing" a fact to different classes is a bit more complex, than using constants. This project where I had this problem is a large project with tens of thousands rows of code. Before I did not use these "centralized constants" and I had to waste much time in trying to find every spot where I had to remember to update the last value of current year, month, quarter, Euro limit in reporting etc. Always I did forget some spot and had start parts of data processing from the start (some parts take hours to finish). This project produces when activated around 3000 different html pages and thousands of picture files of Finland's export and import data. The databases are over 2 GB in size. So the need of solution "for isolating the central parameters" is purely practical. In VIP version 5 we had global facts. They would be still handy for purposes like this in question. :-)
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

I now understand the scope of your problem!

That's curious about the numeric constants ... I'll take a look at that.

By the way, I usually set up a small class named "app" that handles quirky little configuration parameters, functions, etc. that need global access or storage for each application. It is unique to each application. "App" handles things that are defined at compile time; I use a separate standard "config" class for operating parameters that the user can modify.
User avatar
George
Active Member
Posts: 47
Joined: 19 Sep 2011 8:54

Unread post by George »

Use the properties varianble for global access..

If you define a properties varible in .cl file - you can access them globally..

Any personal support reach me to my personal email id..
Kind Regards,
George Ananth. S | Prolog Developer
georgeananth.prolog@gmail.com
+91 9791499282
B.Hooijenga
VIP Member
Posts: 57
Joined: 11 Jul 2002 23:01

Unread post by B.Hooijenga »

Hello Kari,

About cryptic code: I changed the predicatename makeMonths()3 into makeQuarters()/3.
Because that is what this function does: it makes a list of quarters for normal years and also for the lastknown year.
So I needed a predicate which stops the counting to the lastKnownQuarter in the last known year.
You were almost there:

Code: Select all

predicates     goCounting:(). clauses     goCounting():-         foreach YEAR= std::fromTo(2002,lastKnownYearInQuarters) do             foreach Q = list::getMember_nd(["Q1","Q2","Q3","Q4"]) do                 Quarters = makeQuarters(["Q1","Q2","Q3","Q4"],[]),                 if checkQuarter(YEAR,Q,Quarters) = true() then %            doCounting ....                stdio::write(Year," ",Q,"\n")             end if         end foreach     end foreach.     predicates     makeQuarters:(string*,string*) -> string*. clauses     makeQuarters([lastKnownQuarter|_] ,T) = [lastKnownQuarter|T] :-!.     makeQuarters([H|R],T) = makeQuarters(R,[H|T]) :-!.     makeQuarters(_,_) = _ :-         exception::raise_error().   predicates     checkQuarter:(unsigned Year, string Quarter,string* Quarters) -> boolean. clauses     checkQuarter(Year,_,_) = true():-         Year < lastKnownYearInQuarters,!. % A normal year     checkQuarter(Year,Q,Quarters) = true():-            Year = lastKnownYearInQuarters,            list::isMember(Q,Quarters),!. % lastknown year and valid quarter     checkQuarter(_,_,_) = false().
Kind regards
Ben
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

The compiler is optimizing code in several ways.

For code like this:

Code: Select all

clauses     p() :-         fail,         q().
The compiler will not generate the code for calling q, because that part of the code can never be reached anyway. However having code that cannot be reached is likely to be caused by a programming error. Hence the warning.

The code in this mail have kind of problem, though in a slightly more complex form.

We are not able to destinguish whether such unreachable code is in deed the result of a programming error or intensional like here.

Any warning xxx can be switched of completely by adding the compiler argument /w:xxx- (here /w:651-).

I agree that this code use constants to make conditional compilation. Not because it uses constants, but because it uses them to at compile time choose between different parts of the code.

I would suggest that you (rather than disabling the warning) create a little settings/configuration module, which stores the settings in facts.

Code: Select all

class config   properties     lastKnownYearInQuarters  : unsigned (o).     lastKnownQuarter : string (o).   end class config
As benefit of this you will also be able to save and consult the configuration rather than recompling the program.

(Also all the mentioned code will then become reachable, because the lastKnownQuater is unknown at compile time.)
Regards Thomas Linder Puls
PDC
Post Reply