[R-pkg-devel] Trying to work around missing functionality

J C Nash profjcn@@h @ending from gm@il@com
Tue Aug 28 21:15:44 CEST 2018


Thanks for this. Also Duncan's description of how code in the R directory is executed.

I've more or less figured out a workaround. Unfortunately Georgi's solution doesn't quite
do the trick. Here is my current understanding and solution.

Issue: I want to get root of a function of 1 parameter x, but there may be exogenous data Xdata.
       I also want to count the evaluations.
       Some rootfinders don't have counter and some don't allow "..."

Initial solution: Create global envroot with counters and such. Put name of function and
gradient in there, then use a dummy FnTrace (and possibly grTrace). This gave various
check complaints about globals etc. However, does appear to work.

Present approach: Slightly less flexible, but no complaints.
   Within rootwrap() which calls different rootfinders according to method="name", define
   FnTrace and grTrace, set up a list glist for the items I want to share, then
   envroot <- list2env(glist)

The FnTrace and grTrace are defined before the calls to rootfinders, so envroot can be found.
No globals. R CMD check is happy. However, I must call rootfinders via the wrapper, which is
actually simpler from point of view of syntax.

I've still some testing and tweaking, but I think main issues resolved by this.

Thanks to all who responded.

JN







On 2018-08-28 12:27 PM, Georgi Boshnakov wrote:
> If you don't insist on putting the variable in the global environment, 
> variations of the following give a cleaner solution:
> 
> TraceSetup_1 <- local({
>     ifn = 0
>     igr = 0
>     ftrace = FALSE
>     fn = NA
>     gr = NA
> 
>     function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
>         ifn    <<- ifn
>         igr    <<- igr
>         ftrace <<- ftrace
>         fn     <<- fn
>         gr     <<- gr
>         parent.env(environment())
>     }
> })
> 
> For example,
> 
> TraceSetup_1 <- local({
> +     ifn = 0
> +     igr = 0
> +     ftrace = FALSE
> +     fn = NA
> +     gr = NA
> +     function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
> +         ifn    <<- ifn
> +         igr    <<- igr
> +         ftrace <<- ftrace
> +         fn     <<- fn
> +         gr     <<- gr
> +         parent.env(environment())
> +     }
> + })
>>
>> e <- TraceSetup_1(fn = function(x) x^2)
>> ls(e)
> [1] "fn"     "ftrace" "gr"     "ifn"    "igr"   
>> e$fn
> function(x) x^2
> 
> ## let's change 'fn':
>> e$fn <- function(x) x^4
>> e$fn
> function(x) x^4
> 
> 
> Note that the environment is always the same, so can be accessed from anywhere in your code:
> 
>> e2 <- environment(TraceSetup_1)
>> e2
> <environment: 0x000000000d1af620>
>> identical(e2, e)
> [1] TRUE
>>
> 
> If you need a new environment every time, a basic setup might be:
> 
> TraceSetup_2 <- local({
>     staticVar1 <- NULL
>     ## other variables here
>     
>     function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
>         ## force evaluation of the arguments
>         ifn    
>         igr    
>         ftrace 
>         fn     
>         gr     
>         environment()
>     }
> })
> 
> There is no need for local() here but usually one needs also some static variables.
> Now every call gives a different environment  (but all have the same parent):
> 
> ea <- TraceSetup_2(fn = function(x) x^2 - 2*x + 1)
>> ls(ea)
> [1] "fn"     "ftrace" "gr"     "ifn"    "igr"   
>> ea$fn
> function(x) x^2 - 2*x + 1
>>
>> eb <- TraceSetup_2(fn = function(x) x^2 + 1)
>> eb$fn
> function(x) x^2 + 1
>>
>> ## ea$fn is still the same:
>> ea$fn
> function(x) x^2 - 2*x + 1
>>
> 
> Obviously, in this case some further arrangements are  needed for the environments to be made available to the external world.
> 
> Hope this helps,
> Georgi Boshnakov
> 
> 
> -----Original Message-----
> From: R-package-devel [mailto:r-package-devel-bounces using r-project.org] On Behalf Of J C Nash
> Sent: 28 August 2018 14:18
> To: Fox, John; Richard M. Heiberger
> Cc: List r-package-devel
> Subject: Re: [R-pkg-devel] Trying to work around missing functionality
> 
> Indeed, it appears that globalVariables must be outside the function. However, I had quite a bit of
> fiddle to get things to work without errors or warnings or notes. While I now have a package that
> does not complain with R CMD check, I am far from satisfied that I can give a prescription. I had
> to remove lines in the rootfinder like
>    envroot$fn <- fn
> that were used to set the function to be used inside my instrumented function, and instead
> call TraceSetup(fn=fn, ...) where a similar statement was given. Why that worked while the direct
> assignment (note, not a <<- one) did not, I do not understand. However, I will work with this for
> a while and try to get a better handle on it.
> 
> Thanks for the pointer. As an old-time programmer from days when you even set the add table, I'm
> still uncomfortable just putting code in a directory and assuming it will be executed, i.e., the
> globals.R file. However, I now have this set to establish the global structure as follows
> 
>> ## Put in R directory. 
>> if(getRversion() >= "2.15.1") { utils::globalVariables(c('envroot')) } # Try declaring here 
>> groot<-list(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA, label="none")
>> envroot <- list2env(groot) # Note globals in FnTrace
> 
> Then TraceSetup() is
> 
>> TraceSetup <- function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
>>    envroot$ifn <- ifn
>>    envroot$igr <- igr
>>    envroot$ftrace <- ftrace
>>    envroot$fn <- fn
>>    envroot$gr <- gr
>>    return()
>> }
> 
> and it is called at the start of the rootfinder routine.
> 
> Thus I am establishing a global, then (re-)setting values in TraceSetup(), then
> incrementing counters etc. in the instrumented FnTrace() that is the function for which I find
> the root, which calls fn() given by the "user". Messy, but I can now track progress and measure
> effort.
> 
> I'm sure there are cleaner solutions. I suggest offline discussion would be better until such
> options are clearer.
> 
> Thanks again.
> 
> JN
> 
> 
> 
> On 2018-08-28 12:01 AM, Fox, John wrote:
>> Hi John,
>>
>> It's possible that I didn’t follow what you did, but it appears as if you call globalVariables() *inside* the function. Instead try to do as Richard Heiberger suggested and place the call outside of the function, e.g., in a source file in the package R directory named globals.R. (Of course, the name of the source file containing the command isn’t important.)
>>
>> I hope this helps,
>>  John
>>
>> -----------------------------------------------------------------
>> John Fox
>> Professor Emeritus
>> McMaster University
>> Hamilton, Ontario, Canada
>> Web: https://socialsciences.mcmaster.ca/jfox/
>>
>>
>>
>>> -----Original Message-----
>>> From: R-package-devel [mailto:r-package-devel-bounces using r-project.org] On
>>> Behalf Of J C Nash
>>> Sent: Monday, August 27, 2018 8:44 PM
>>> To: Richard M. Heiberger <rmh using temple.edu>
>>> Cc: List r-package-devel <r-package-devel using r-project.org>
>>> Subject: Re: [R-pkg-devel] Trying to work around missing functionality
>>>
>>> Unfortunately, makes things much worse. I'd tried something like this already.
>>>
>>>> * checking examples ... ERROR
>>>> Running examples in ‘rootoned-Ex.R’ failed The error most likely
>>>> occurred in:
>>>>
>>>>> ### Name: rootwrap
>>>>> ### Title: zeroin: Find a single root of a function of one variable within
>>>>> ###   a specified interval.
>>>>> ### Aliases: rootwrap
>>>>> ### Keywords: root-finding
>>>>>
>>>>> ### ** Examples
>>>>>
>>>>> # Dekker example
>>>>> # require(rootoned)
>>>>> dek <- function(x){ 1/(x-3) - 6 }
>>>>> r1 <- rootwrap(dek, ri=c(3.0000001, 6), ftrace=TRUE,
>>>>> method="uniroot")
>>>> Error in registerNames(names, package, ".__global__", add) :
>>>>   The namespace for package "rootoned" is locked; no changes in the global
>>> variables list may be made.
>>>> Calls: rootwrap -> TraceSetup -> <Anonymous> -> registerNames
>>>> Execution halted
>>>
>>> Also had to use utils::globalVariables( ...
>>>
>>> JN
>>>
>>>
>>> On 2018-08-27 08:40 PM, Richard M. Heiberger wrote:
>>>> Does this solve the problem?
>>>>
>>>> if (getRversion() >= '2.15.1')
>>>>   globalVariables(c('envroot'))
>>>>
>>>> I keep this in file R/globals.R
>>>>
>>>> I learned of this from John Fox's use in Rcmdr.
>>>>
>>>> On Mon, Aug 27, 2018 at 8:28 PM, J C Nash <profjcnash using gmail.com>
>>> wrote:
>>>>> In order to track progress of a variety of rootfinding or
>>>>> optimization routines that don't report some information I want, I'm
>>>>> using the following setup (this one for rootfinding).
>>>>>
>>>>> TraceSetup <- function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){ #
>>>>> JN: Define globals here
>>>>>    groot<-list(ifn=ifn, igr=igr, ftrace=ftrace, fn=fn, gr=gr, label="none")
>>>>>    envroot <<- list2env(groot) # Note globals in FnTrace
>>>>>    ## This generates a NOTE that
>>>>>    ## TraceSetup: no visible binding for '<<-' assignment to ‘envroot’
>>>>> ##   envroot<-list2env(groot, parent=.GlobalEnv) # Note globals in FnTrace -
>>> - this does NOT work
>>>>>    ## utils::globalVariables("envroot") # Try declaring here --
>>>>> causes errors # end globals
>>>>>    envroot
>>>>> }
>>>>>
>>>>> FnTrace <- function(x,...) {
>>>>>   # Substitute function to call when rootfinding
>>>>>   # Evaluate fn(x, ...)
>>>>>     val <- envroot$fn(x, ...)
>>>>>     envroot$ifn <- envroot$ifn + 1 # probably more efficient ways
>>>>>     if (envroot$ftrace) {
>>>>>        cat("f(",x,")=",val," after ",envroot$ifn," ",envroot$label,"\n")
>>>>>     }
>>>>>     val
>>>>> }
>>>>>
>>>>>
>>>>> Perhaps there are better ways to do this, but this does seem to work quite
>>> well.
>>>>> It lets me call a rootfinder with FnTrace and get information on evaluations
>>> of fn().
>>>>> (There's another gr() routine, suppressed here.)
>>>>>
>>>>> However, R CMD check gives a NOTE for
>>>>>
>>>>>   TraceSetup: no visible binding for global variable ‘envroot’
>>>>>   Undefined global functions or variables:
>>>>>     envroot
>>>>>
>>>>> The commented lines in TraceSetup suggest some of the things I've
>>>>> tried. Clearly I don't fully comprehend how R is grinding up the
>>>>> code, but searches on the net seem to indicate I am far from alone. Does
>>> anyone have any suggestion of a clean way to avoid the NOTE?
>>>>>
>>>>> JN
>>>>>
>>>>> ______________________________________________
>>>>> R-package-devel using r-project.org mailing list
>>>>> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>>>
>>> ______________________________________________
>>> R-package-devel using r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-package-devel
> 
> ______________________________________________
> R-package-devel using r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>



More information about the R-package-devel mailing list