Wiki for discussion of the scope (local vs. global) of CASA variables.


4/08: an attempt to identify specific behaviours that should(not) be present : ScopeUseCases

Variable Scope Options

There appear to be four main options: Non-persistent Local, Persistent Local, Global, and Scope set by Syntax.

Global

  • AIPS mode
  • variable remembers its value whenever it is set until it is changed, regardless of which tasks is used
  • currently the default for CASA. Can be explicitly invoked by typing casaglobals=True
  • PROS:
    • easy to set values outside a task
    • scripts are easier to read
  • CONS:
    • variables with perfectly good defaults in some tasks get reset (possibly to invalid values) if variable is set for any other task.
    • can screw up scripts if user forgets that a task uses a parameter that was set based on another task. I could see this being hard to uncover if you are not familiar with casa, leading to user frustration.

Non-persistent Local

  • IDL macro mode
  • variables always revert to default when a given task is run.
  • currently invoked by typing casaglobals=False.
    • This requires you to specify any arguments that you want different from the default, each time you execute a task.
    • It doesn't take effect until you execute the task once (variables are set to default at the completion of the task running, not before).
  • can also be done on a per-task basis by running the default() function before invoking the tasks, e.g. default('imhead')
  • PROS:
    • much less typing when most parameters have reasonable defaults
    • its clear what values non-default variables will have for a tasks, since they have have to be set explicitly
  • CONS:
    • onerous if you run a task over and over again (but in this case, use casaglobals=True, default the task you want, set variables outside, and run over and over again).

Persistent Local (a.k.a. "sticky parameters")

  • iraf mode
  • variables remember the value they were assigned in a given tasks the last time the tasks was run, but are not transferred across tasks (e.g. running imhead with mode="list" will not change mode in almasimmos (for which "list" is not a valid value for mode).
  • PROS:
    • Makes running tasks over and over again easy
    • avoids parameters taking on non-sensical values
  • CONS:
    • can already do this on a per-task basis using the tget command (does that work within scripts?)
    • I think this rules out simple parameter setting outside a command line (see John's comments in discussion)
    • Would require development work to implement

Scope set by Syntax (1)

  • python (ish) mode
  • Variables set at the top level are global, and ones set inside function or task calls are local to that call. e.g.:
          imagename = 'mona_lisa'        (Hark!  A global is initialized.)
          imhead()                       (runs using imagename='mona_lisa')
          print imagename
            mona_lisa                    (It would have been destroyed by casaglobals=False or default().)
          imhead(imagename='guernica')
          imhead('m51')                  (imhead's first parameter is imagename.)
          print imagename
            mona_lisa                    (CASA currently would have set it to 'm51')
          
  • PROS:
    • More pythonic, so it should make scripters happy, especially if they are editing their scripts with python-aware software. We still want tasks to check the globals for each unspecified parameter, though (current behavior).
    • Using positional or named arguments does not have the side effect of assigning values to global variables.
    • It is close to CASA's current behavior, but corrects what I (RR) think is a bizarre quirk.
  • CONS:
    • You'd no longer be able to assign values to global variables using task calls?
    • Would need some modification of CASA.

Scope set by Syntax (2)

this is based on David Whysong's suggestion, and he maintains that it is orthogonal to the options above. i.e., one can make decisions of what the local scope of variables should be and still have these two modes
  • variables have global or local scope, depending on how a task is invoked
  • Anticipate that global-mode would be used interactively, local in scripts to make sure only those parameters that you want changed from defaults get changed
  • Task execution using sticky parameters:
    • [set up parameters]
    • go [task]
    • [task] has variables set to whatever their values were when go was typed
    • this is how casa works now
  • Task execution without use of sticky parameters:
    • task(var1=value1, var2=value2, var3=value3, etc.) has all variables set to default values, except var1, var2, var3, etc.
    • Could separately make a decision whether these variables are sticky or not (whether they are remembered the next time the task is run)
  • PROS:
    • less typing - don't need to keep "defaulting" tasks to make sure some parameter is inadvertently set.
    • scripts become easier to read/write
    • don't need to inelegantly swap modes by setting casaglobals=True/False, which might make script cutting and pasting not work.
    • In general, removes the two modes from CASA, which would mean users would have to always be aware of what mode they are in when reading examples, copying scripts, running interactively, etc.
    • pasting from scripts should work with no problems at all
  • CONS:
    • requires developer work
    • still have global mode, so that tasks can inadvertently have parameters inherit values set for completely different reason (so might still need to default(tasks))
    • not exactly intuitive (but at least it would be consistent, vs. the casaglobals)


Discussion

Joe's email on CASA's treatment of Pure Local/Global [3/26/2008]

Essentially, casaglobals is a toggle to determine whether you want the globals/local
behavior.

Normally, one can set:
vis='filename'
and then it will continue to be used throughout the session until changed.

casaglobals=False
requires you to specify any arguments that you want different from the default, each time
you execute a task. It is implemented by clearing the values after each task execution.
e.g.,

CASA <6>: casaglobals                           #default behavior                                                        
  Out[6]: True

CASA <7>: casaglobals=False                  # use local behavior                                     

CASA <8>: listobs vis='ALMASIM.ms'        # runs okay                                                 
--------> listobs(vis='ALMASIM.ms')

CASA <9>: listobs                                     # fails because vis has not been specified                
--------> listobs()
*** Error *** Visibility data set not found - please verify the name

CASA <10>: casaglobals=True                                                                                 

CASA <11>: listobs vis='ALMASIM.ms'                                                                         
---------> listobs(vis='ALMASIM.ms')

CASA <12>: listobs                                   # runs okay because vis is global                              
---------> listobs()

Joe

Remy's email on Persistent Locals [3/27/2008]

IMHO There are two kinds of parameters (roughly) - data specification
(filenames, etc), and task control (mode, deconv alg to use etc).
Sometimes it's useful for the former to be global, usually its a
nuisance for the latter. 

example from this morning:  imhead(mode="list") followed by almasimmos,
which also has a mode, as in mode=continuum, but you get mode=list,
which is nonsensical. 

-> parameters having to do with controlling how a task works should be
local and persistent during a session.

In the interest of simplicity, we should make all parameters local and
persistent (i.e. you set it in a task, it stays that way for that task
until you change it). (implementing two flavors of parameters probably
just invites disaster)

-> we should probably just have one [advertised] mode, for simplicity of
support and development.  

I propose, if it doesn't cause implementation issues I haven't thought
of, to have global and local (scope=task) persistent variables, and when
you run a task, the global ones that correspond to task parameters get
unset, e.g.

mode="list"  # global, since the system doesn't know what task we are
thinking about, although the user presumably has one in mind.

imhead(imagename="foo.image") # at this point the "mode" that's local to
imhead gets set to "list", and the global one gets unset

almasimmos() # uses defaults since nothing has been set

almasimmos(mode="continuum") # sets local "mode" in almasimmos, global
"mode" stays unset

imhead(imagename="foo2.image") # remembers from before that
mode="list" (and certainly not mode="continuum" which doesn't make
sense)

$0.05
-r

Rob's comment on Remy's email [3/27/2008]

At 10:50 AM EDT on March 27 Remy Indebetouw sent:
> > 
> > example from this morning:  imhead(mode="list") followed by almasimmos,
> > which also has a mode, as in mode=continuum, but you get mode=list,
> > which is nonsensical. 
> > 

I've come across that a fair bit myself.  For better or worse, some parameters
are more naturally global than others, and mode, at least for these 2 tasks
would be better off local.  I'm not necessarily advocating that some parameters
be declared as global and some as local, but it is a possibility.  Ideally the
scope of a parameter would be common sense, but a user could always tell by
checking whether the parameter in question is an attribute of par.  (Run
help(par.mode) for yourself, and you'll get an idea of what I'm talking about.
Note that the almasimmos definition of mode isn't included.)

An alternative I know would be easy to implement for cases like mode in imhead
and almasimmos, would be to use the globally set value if it is valid, but otherwise
locally substitute the task's default value.

i.e.

CASA <10>: mode = 'list'
CASA <11>: imhead()       # Uses list
CASA <12>: inp(almasimmos)
# Currently, mode shows up as 'list' highlighted in red, since it's an error.
# almasimmos knows that, so it could substitute its default:
mode                =  'channel'

# I admit substituting 'channel' here without setting up things like nchan will
# still probably do the wrong thing, and maybe even waste more time doing so.
CASA <13>: almasimmos


# Still uses list, not 'channel', or the imhead default of 'get'.
CASA <14>: imhead()

# Now things get interesting, since 'list' is a valid value for mode in both
# imhead and flagmanager.  So, if careless (let's pretend you are), you could
# run imhead or flagmanager with mode='list' when you did not want to.
# Fortunately list is pretty safe, the way defaults should be if at all
# possible.
CASA <15>: flagmanager()

> > I propose, if it doesn't cause implementation issues I haven't thought
> > of, to have global and local (scope=task) persistent variables, and when
> > you run a task, the global ones that correspond to task parameters get
> > unset, e.g.

Do you really want them to be unset, or just ignored by the current task?
The former behavior seems very global unfriendly.

> > mode="list"  # global, since the system doesn't know what task we are
> > thinking about, although the user presumably has one in mind.

Not necessarily, since the user might have run inp(imhead), suggesting that
imhead is the task du jour.

But having
CASA <16>: mode = 'list'
being anything other than setting mode in the topmost scope would not be
pythonic.  Python has well defined and reasonable scoping rules, and I think it
would be a good idea to use them:

CASA <17>: mode = 'continuum'
CASA <18>: imhead  # Error, 'continuum' isn't valid for imhead.
CASA <19>: imhead(mode='list')
CASA <20>: print mode
continuum

-- Rob Reid 

John's comments on persistent locals [3/27/2008]

I like persistent locals, but I do see a problem - in Remy's example, how does almasimmos() know not to inheret the value of mode that was set on the command line? e.g.:
CASA <1>: mode="list"
CASA <2>: imhead()     # how does imhead know it should inherit the value of mode? 
CASA <3>: almasimmos() # how does this know that it should NOT inherit the value of mode?

vs.

CASA <1>: mode="continuum"
CASA <2>: almasimmos() # how does imhead know it should inherit the value of mode? 
CASA <3>: imhead()     # how does this know that it should NOT inherit the value of mode?

Remy's solution is that it only inherits variables that have validly set values. But this is not a strictly local-sticky scope e.g.:

CASA <1>: flagdata(spw='0:10 15')
CASA <1>: spw='3-27'
CASA <2>: bandpass()    # will use spw='3-27'
CASA <3>: flagdata()    # would use spw='3-27', but sticky-parameter definition would want it to use spw='0: 10 15'
I think this sometimes local-sticky, sometimes global behavior is a deal breaker.

It seems to me that you would need to specify any variables you want to set for a task via task(var1="value1", var13="value12",...). Thats fine, but it becomes cumbersome when running tasks that have many parameters. This would work much better when we have a mode where we can edit task parameter inputs in-line (like epar in iraf), but would still make scripts harder to read and write.

An option would be to have the ability to set variables for a task outside the task, e.g.:
CASA <1>: imhead.mode="list"
CASA <2>: clean.mode="mfs"
CASA <3>: imhead()     # runs with mode="list" as set above
CASA <4>: clean()      # runs with mode="mfs" as set above 
CASA <5>: almasimmos() # runs with default value of mode, since it was not reset.
(this probably violates some python syntax).

Another way to do this may be to have "run=false" option for tasks (^D option in iraf), just to set parameters. e.g.
CASA <1>: clean(vis='ngc5921.ms', run=F)
CASA <2>: clean(mode='mfs', run=F)
CASA <3>: clean(alg='csclean', run=F)
CASA <4>: clean(niter='1000', run=F)
CASA <5>: clean()

You might also then want a way to set the same variable across ALL tasks, eg.
CASA <1>: setglobal(nchan=127, bchan=13, echan=121)
would set nchan, bchan, echan to the above values in ALL tasks where these are variables.

My vote: lets stick to what is there, and encourage people to use the default function often if running in casaglobals=False mode. Its simple to describe, you know what it does, it doesn't require any developers time. The main drawback I see is that novice users might write long scripts that set some variable at some time, not realizing that that value is used in a way they did not intend for some other task way down in the script. They could easily get a result that is not what they intended, but not obviously wrong either.

Parameter Sets (RobReid, Apr. 11, 2008)

Sometimes it is desirable to deal with groups of parameters. As John noted above, tget already allows this to some extent. run_task_with_dict.py lets you run tasks (or functions!) with some or all the parameters batched into dictionaries. Dictionaries can be easier to work with than tget's savefiles, especially from inside scripts. With prameter sets you can have things like quikclean_pset for cleaning a small field with big pixels, and slowclean_pset for cleaning a big field with small pixels, and swap them in an out to your heart's content.

Since run_task_with_dict() is just a wrapper function in a python file, parameter sets are completely optional, and no changes to CASA are necessary. The catch of the not-tinkering-with-CASA approach is that after running run_task_with_dict('taskname', {'param1': 42.0, 'param2': 'Hi'})), param1 and param2 will be respectively set to 42.0 and 'Hi' at the global level. That is a side effect of the way tasks run. run_task_with_dict() could undo that effect, but I am hopeful that the behavior of the tasks will be changed.

RobReid, Apr. 15: I just realized Python's func(*positional_args, **kwargs) syntax makes the above seem like overkill, i.e. if pargs is the tuple ('my_image',), and imh_pset is {'mode': 'list', 'async': True}, you can run imhead like this: imhead(*pargs, **imh_pset)

run_task_with_dict.py, however, comes with automatic defaulting and saving/loading parameter sets to/from files. It also correctly handles parameter sets which include parameters that don't apply to the function or task being called.


-- JohnHibbard - 27 Mar 2008
Topic revision: r10 - 2013-02-13, PatrickMurphy
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding NRAO Public Wiki? Send feedback