Skip to main content

packages - Automated testing for compatibility with older Mathematica versions


I have several packages which I actively develop and maintain. I try to stay up to date with new releases of Mathematica and usually update within a couple of months of a new version coming out. As a result, I can always be sure that my packages work with the latest version of Mathematica. However, not all users of my packages will want or even be able to update when new releases come out. I would like to support these users, but doing so is difficult when all my development and testing happens with only the latest version.


Is there an automated way of checking which version of Mathematica is required by my packages? In particular, I would like to know if I am using functionality which was only introduced in a specific version of Mathematica. Some specific examples I have encountered are:



  • DeleteDuplicates only appeared in version 7.0

  • SeriesCoefficient had a change in behaviour in some cases in version 7.0.

  • Wavelet support was added in version 8.0


I am aware that the documentation usually says when a feature is new in a specific version, but some of my packages are quite large and it would not be feasible to manually check the reference pages for each function which is used. The NewInXXAlphabeticalListing guides would be less effort to check, but I would prefer an automated solution.


Once I know which version is required I can add a check against $VersionNumber when my package is loaded and output an warning if the version being used is to old.




Answer



The only reliable way seems to have a good set of unit test suites, and run them in earlier versions of Mathematica (I mention this here since the answer and comments mentioning this were deleted). However, having explicit rules for when functions were introduced and / or last changed, extracted from the docs, seems to me a good thing, which may help reduce some work, give hints, etc. So, in addition to the suggestions in other answers / comments (I particularly support the unit testing suggestion), the following code can be executed to extract the versioning information from the documentation:


ClearAll[getVersionSince];
getVersionSince::fail = "Unable to extract version information for function `1`";
getVersionSince[file_String?FileExistsQ] :=
With[{nb = NotebookOpen[file, Visible -> False]},
With[{res =
Cases[
NotebookGet[nb],
Cell[s_String, "History", ___]:>

StringCases[StringTrim@s,
{"New in" ~~ (Whitespace | "") ~~ d : ((DigitCharacter | ".") ..) ~~ __ ~~
"Last modified in" ~~ (Whitespace | "") ~~ m : (((DigitCharacter | ".") ..)) :>
{d, m},
"New in" ~~ (Whitespace | "") ~~ d : ((DigitCharacter | ".") ..) :> d}
],
Infinity]},
NotebookClose[nb];
First@res /; res =!= {}] /; nb =!= $Failed];


getVersionSince[file_String?FileExistsQ] :=
(Message[getVersionSince::fail, Style[FileBaseName[file], Red]]; $Failed);

Here is the setup I used:


$docdir = 
FileNameJoin[{$InstallationDirectory, "Documentation", "English",
"System", "ReferencePages", "Symbols"}];
$functions = FileNames["*.nb", {$docdir}];

To produce the rules for the functions, you can use something like



rules = Table[FileBaseName[f] -> getVersionSince[f],{f, $functions}]

I actually used


j = 0;
Monitor[
functionRules =
abortableTableAlt[(j++; FileBaseName[f] -> getVersionSince[f]), {f, $functions}],
j]

where the abortable table function abortableTableAlt is described at the bottom of this answer. The process was time and memory-consuming, so I saved the result to a file, available from this gist. The result has this format:



{"AbelianGroup" -> {"8"}, "AbortKernels" -> {"7"}, "Abort" -> {"2"}, 
"AbortProtect" -> {"2"}, "Abs" -> {"1"}, "AbsoluteCurrentValue" -> {"6"},
"AbsoluteDashing" -> {{"2", "6"}}, "AbsoluteFileName" -> {"7"}, << 3400 >>,
"$UserName" -> {"3"}, "$Version" -> {"1"}, "$VersionNumber" -> {"2"}, "$$Media" -> {"1"}}

Where a single number (string) is the earliest version when the function (or at least the doc.page) was introduced, and the format like {{"2","6"}} means that it was introduced in v.2 and last modified in v.6. In total, 3412 notebooks were processed. The only function for which this code failed was KConnectedComponents.


You can import the file from the mentioned gist, and I'd recommend to use Dispatch to speed up the rule application. Alternatively, you can use Save to attach these rules to whatever function (symbol) you want. As was suggested in another answer, this can be combined with some tool to extract dependencies, which would give you a list of system symbols used in your code. One such tool was developed David Wagner way back in 1996 and desribed in his article in the Mathematica Journal. I developed a similar tool here, but it is a work in progress and contains some bugs as of now.


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

equation solving - Invert and fit implicitly defined curve

I need to fit an implicitly defined curve. I thought I could get some data out of Solve , and then using FindFit . Therefore, I would like to find the relation the parametric curve defined by $F(x,y)=0$: Solve[-(1/2) + 1/2 (0.41202 BesselK[0, 0.1 Sqrt[x^2 + y^2]] + (0.101483 x BesselK[1, 0.1 Sqrt[x^2 + y^2]])/Sqrt[x^2 + y^2]) == 0, y] But I can't get an output: Solve was unable to solve the system with inexact coefficients or the system obtained by direct rationalization of inexact numbers present in the system. Since many of the methods used by Solve require exact input, providing Solve with an exact version of the system may help. >> Edit: In particular, I would like to fit the data coming from the curve with the expression of another curve, and not with a function $f(x)$. In particular, since this clearly looks like a cardioid , I would like it to fit to something like it. What other strategies could I try?

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...