Sunday, May 31, 2015

Use Contexts (Namespaces) in Mathematica to Organize Symbols and Prevent Collisions

Context is the Mathematica term for the programming device, namespace, which serves to isolate and organize related groups of Symbols and protect Symbols having the same name from conflict. Context paths and names, like file paths and names, are Strings.

Context[]//FullForm

"Global`”

You do not need to declare a Context before entering one. As usual with Symbols, Mathematica cuts out the extra and superfluous declaration step.

Contexts[] and $Context give the same result but have two different purposes. Contexts[] is used to tell the current Context[] and that of a Symbol (Context@aSymbol), while $Context is the environment variable where the current Context is stored. Always use Begin, BeginPackage, End, and EndPackage to  change Context and $Context; do not change $Context directly.

The backtick “`” is called a Context mark and delimits nested Contexts from each other, just like backslashes delimit directory hierarchies in Windows, forward slashes in web URLs and Unix, periods in domain names, colons in Macintosh directories, and in general various characters separating records and fields in data formats. All Contexts and Package names must end with a Context mark :

“MyPackage`SubPackage`”

But as in some hierarchy systems omitting the initial Context mark indicates a Context at the highest level, like the Global` or System` Contexts, while a Context mark preceding a Context name indicates a sub-Context of the current Context.

While it's rarely done, we can access any Symbol in any Context by using its full Context path name. I suspect Mathematica always sees full Symbol path names, thereby seeing all Symbols in all Contexts, and resolving apparent conflicts between identical 'nicknames' — the final Context path element only — that we may see. The following command will return the value, not of any Global value x, or the value of a Symbol x in any other Context, but the value of x in the Context TestPackage`Private`. Note this usage does not specify the Context path as a String.

TestPackage`Private`x

To change Contexts use Begin@”NewContext`” (again, preceding the Context name with a backtick Context mark if you want to make NewContext` a sub-Context of the current one).

Begin@”NewContext`”

"NewContext`"

Changing Contexts creates a push-down stack listing them. To leave NewContext`, use End[] (no Context name is necessary) and the current Context pops off the top of the stack and you re-enter the Context you were in before NewContext`. On leaving a Context Mathematica tells you what Context you were in, not which one you’re entering. To see what Context you’re in, use Context[].

End[]

NewContext`

Context[]

Global`

Just as $Path shows the sequence of directories searched by Mathematica for files, $Context shows the sequence of Contexts searched by Mathematica for symbols.

$ContextPath

{"TestPackage`", "TemplatingLoader`", "PacletManager`", "Global`", "System`"}

Mathematica attempts to order Contexts in $Path so that smaller Contexts with more specific commands, such as user-created ones, are searched before larger Contexts with more general commands, such as the ~5000 built-in commands in System` and Packages auto-loaded by Mathematica.

In a composition of commands, Mathematica uses $Context before it is changed by a command within a line. Therefore always put Begin and BeginPackage on their own line.

Friday, May 29, 2015

String Processing: StringCases and StringMatchQ

I recently got inconsistent results from StringCases and StringMatchQ (and used Pick instead of Cases). When using Cases or Select, you typically debug issues by mapping MatchQ onto the List that you're sifting for patterns. Similarly, StringMatchQ is the tool used to debug StringCases. However - while I don't believe there is any difference between patterns accepted by MatchQ vs. Cases, there is a significant difference between patterns accepted by StringMatchQ vs. StringCases.

StringMatchQ will accept what are called 'abbreviated String metacharacters'. These are the asterisk wildcard "*" for 0 or more characters, the @ wildcard for one or more non-upper case characters ("@@@@" matches any 4-non-upper case character Expression), and double-backslashes for literal characters (e.g. \\* to match an asterisk).

But StringCases will not; it will only accept StringExpression patterns or RegularExpressions. Here is an example. I want to extract all the "VCathode*" values from this datafile header:

data = {"%", "cln1x", "V", "(V)", "@", "VCathode=2.75392", "V", "(V)", "@", "VCathode=2.29025", "V", "(V)", "@", "VCathode=1.86092", "V", "(V)", "@", "VCathode=1.59846", "V", "(V)", "@", "VCathode=1.40122", "V", "(V)", "@", "VCathode=1.28137", "V", "(V)", "@", "VCathode=1.15876", "V", "(V)", "@", "VCathode=1.06451", "V", "(V)", "@", "VCathode=1.0037", "V", "(V)", "@", "VCathode=0.950678", "V", "(V)", "@", "VCathode=0.881652"};

StringCases with an asterisk fails:

In[190]:= StringCases[data,"VCathode*"]
Out[190]= {{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}}

StringMatchQ with an asterisk works:

In[191]:= StringMatchQ[data,"VCathode*"]
Out[191]= {False,False,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True,False,False,False,True}

Here is the solution for StringCases using StringExpressions. I use Flatten to remove nulls ("{}").

In[192]:= StringCases[#,"VCathode"~~___]&/@data//Flatten
Out[192]= {VCathode=2.75392,VCathode=2.29025,VCathode=1.86092,VCathode=1.59846,VCathode=1.40122,VCathode=1.28137,VCathode=1.15876,VCathode=1.06451,VCathode=1.0037,VCathode=0.950678,VCathode=0.881652}

And here is the solution for StringCases using a RegularExpression. Here I use DeleteCases instead of Flatten to remove nulls, but leave sets of List brackets around the returned values.

In[195]:= StringCases[#,RegularExpression["VCathode.*"]]&/@data//DeleteCases[#,{}]&
Out[195]= {{VCathode=2.75392},{VCathode=2.29025},{VCathode=1.86092},{VCathode=1.59846},{VCathode=1.40122},{VCathode=1.28137},{VCathode=1.15876},{VCathode=1.06451},{VCathode=1.0037},{VCathode=0.950678},{VCathode=0.881652}}

Thank you to Xin Xiao from Wolfram Technical Support.



Wednesday, May 27, 2015

An Example Using Pick to Select Data by Pattern Instead of Cases, Select, or StringCases

Pick

Prefatory remark: To learn Mathematica efficiently you should focus on the most-frequently-used functions. I will provide a list of those soon. For most users Pick[] will not be a frequently-used function. You can learn "esoteric" Mathematica functions just for fun or because in your work certain esoteric functions are handy.

Here, nonetheless, is an example spawned from my not being able to get StringCases to work (solved and explained here), altho strangely StringMatchQ does work, which is the standard way to see if StringCases will work.

This is a file header from a parameter sweep I did in the finite element modeling program COMSOL. The goal is to select the different parameters used in the sweep, which are voltages such as "VCathode=2.29025"; I don't need all the other noise in there.

The way the command works is: 1) StringMatchQ is mapped onto data, which is a List of Strings, and returns True if the target matches the String with "*" wildcard, or False otherwise; 2) Pick then scans data to find instances matching the selection pattern. In this case since Pick's default selection pattern is True, I didn't need to use its 3rd argument that specifies a different selection pattern (such as "1" vs. "0").

data = {"%","cln1x","V","(V)","@","VCathode=2.75392","V","(V)","@","VCathode=2.29025","V","(V)","@","VCathode=1.86092","V","(V)","@","VCathode=1.59846","V","(V)","@","VCathode=1.40122","V","(V)","@","VCathode=1.28137","V","(V)","@","VCathode=1.15876","V","(V)","@","VCathode=1.06451","V","(V)","@","VCathode=1.0037","V","(V)","@","VCathode=0.950678","V","(V)","@","VCathode=0.881652","V","(V)","@","VCathode=0.836608","V","(V)","@","VCathode=0.795234","V","(V)","@","VCathode=0.753297","V","(V)","@","VCathode=0.73737","V","(V)","@","VCathode=0.725229","V","(V)","@","VCathode=0.703015","V","(V)","@","VCathode=0.68059","V","(V)","@","VCathode=0.646921","V","(V)","@","VCathode=0.62848"};

Pick[data,StringMatchQ[#,"VCathode*"]&/@data]

{VCathode=2.75392,VCathode=2.29025,VCathode=1.86092,VCathode=1.59846,VCathode=1.40122,VCathode=1.28137,VCathode=1.15876,VCathode=1.06451,VCathode=1.0037,VCathode=0.950678,VCathode=0.881652,VCathode=0.836608,VCathode=0.795234,VCathode=0.753297,VCathode=0.73737,VCathode=0.725229,VCathode=0.703015,VCathode=0.68059,VCathode=0.646921,VCathode=0.62848}

My next step will be to use Table and Join to insert the correct parameter in the header of the its matching data file from COMSOL.

Trivia: Pick was originally called BinarySelect and Stephen Wolfram re-named it, as is his want, to a short Anglo-Saxon term (a large metal rod is called a pick, or think of using toothpick to pick up food). I ran into him at a ice cream shop and complimented him on the function and asked if he had re-named it. When he was much younger Stephen ate a lot of chocolate and didn't take care of himself physically. But that has changed, he is careful with his diet and spends a lot of time on the treadmill staying in good physical condition.