How to evaluate a notebook from the command line?
Asked Answered
P

2

19

How can we evaluate a Mathematica notebook from the command line (i.e. when running the kernel in command line mode)?

Suppose we're working on a remote machine. I know it is possible to convert the notebook to an m-file, and evaluate that, but I'm curious if it's possible to do this directly using the notebook.


This is what I have so far:

First, we need to start a headless X server on the remote Linux machine, so the front end can run there (and open the notebook). Skip this step if you're working on a local machine.

Xvfb :1 &
export DISPLAY=:1

After this I started a Mathematica kernel (math) and did the following.

It's necessary to use UsingFrontEnd because opening notebook requires a front end. test.nb has a single input cell containing a=1.

In[1]:= nb=UsingFrontEnd@NotebookOpen["test.nb"]

Out[1]= -NotebookObject-

After trying to evaluate the notebook, apparently I get a dialog, and I need to use Return[] to return. I am not sure why the input line starts counting from 1 again (a new kernel was started by the front end?) Note that a didn't gain a value.

In[2]:= UsingFrontEnd@NotebookEvaluate[nb]

 In[1]:= a

 Out[1]= a

 In[2]:= Return[]

Out[2]= a

After returning from the dialog, a still doesn't have a value.

In[3]:= a

Out[3]= a
Plash answered 28/12, 2011 at 14:43 Comment(2)
Looks like we're working in a similar way and are equally lazy!Deficient
+1 I'd love to know this, too! I'd love to evaluate my notebooks on a remote system on our HPC.Callista
D
10

This is on Windows, using Arnouds nice work and just adding plain old MathLink (pretty slow btw ...):

link = LinkCreate["8000", LinkProtocol -> "TCPIP"];
UsingFrontEnd[
NotebookPauseForEvaluation[nb_] := Module[{},
 While[ NotebookEvaluatingQ[nb], Pause[.25] ] ];
NotebookEvaluatingQ[nb_]:=Module[{},
 SelectionMove[nb,All,Notebook];
 Or@@Map["Evaluating"/.#&,Developer`CellInformation[nb]]
];
nb = NotebookOpen["G:\\mma\\test.nb"];
SelectionMove[nb, Before, Notebook];
NotebookWrite[nb, Cell["Link = LinkConnect[\"8000\", LinkProtocol -> \"TCPIP\"]", "Input"]];
SelectionMove[nb, After, Notebook];
NotebookWrite[nb, Cell["LinkWrite[Link, a]", "Input"]];
SelectionMove[nb, All, Notebook];
SelectionEvaluate[nb];
a = LinkRead[link];
Print["a = ",a];
]
Debi answered 28/12, 2011 at 18:40 Comment(4)
Thanks! Do we really need LinkProtocol -> "TCPIP" here or can we use the more efficient default? (I think by default it uses memory mapped files for interprocess commuincation)Plash
Probably. At least I cannot get it to work with LinkProtocol -> "SharedMemory"Debi
BTW: Why do you really need this at all? Is it not easier to save your results by DumpSave or some such?Debi
Perhaps he develops code on a laptop and wants to run it on remote machines. That is what I do and I find these answers very useful (and using a notebook rather than an m file is, in my case, so I can mix code and typeset discussion).Deficient
D
11

This is a partial answer to your question. The following code opens a notebook, assigns it a "Test" kernel, evaluates the notebook in that kernel, waits for the evaluation to finish and saves the evaluated notebook. It does not cause a to be defined in the local command line kernel though.

This waits for kernel evaluations to finish in the notebook:

NotebookPauseForEvaluation[nb_] := Module[{},
 While[ NotebookEvaluatingQ[nb], Pause[.25] ] ]

This checks if any cell in the notebook is still under evaluation:

NotebookEvaluatingQ[nb_]:=Module[{},
 SelectionMove[nb,All,Notebook];
 Or@@Map["Evaluating"/.#&,Developer`CellInformation[nb]]
]

This is just a diagnostic message, when you're trying to redefine a kernel like "Test":

AddTestEvaluator::exists = "Evaluator `1` is already defined, but has a definition that is `2` and not the expected `3`.";

This is code to add an evaluator, like "Test" to the frontend:

AddTestEvaluator[evaluator_String] := Module[
 {evaluatornames, testevaluator},
 evaluatornames = EvaluatorNames /. Options[$FrontEnd, EvaluatorNames];
 testevaluator = evaluator -> {"AutoStartOnLaunch" -> False};
 Which[
  MemberQ[evaluatornames, evaluator -> {"AutoStartOnLaunch" -> False}],
  Null,
  MemberQ[evaluatornames, evaluator -> _],
  Message[AddTestEvaluator::exists,
  evaluator,
  evaluator /. (EvaluatorNames /. Options[$FrontEnd, EvaluatorNames]),
  {"AutoStartOnLaunch" -> False}
 ],
 True,
 AppendTo[evaluatornames, testevaluator];
 SetOptions[$FrontEnd, EvaluatorNames -> evaluatornames]
 ]
]

Finally, this is the code to evaluate a notebook under a "Test" kernel and save the evaluated kernel:

 UsingFrontEnd[     
  AddTestEvaluator["Test"];
  nb = NotebookOpen["test.nb"];
  SetOptions[nb,Evaluator->"Test"];
  SelectionMove[nb,All,Notebook];
  SelectionEvaluate[nb];
  NotebookPauseForEvaluation[nb];
  NotebookSave[nb]
 ]

I'm still looking into a solution for your full problem (having a defined in the local command line kernel).

Demonetize answered 28/12, 2011 at 17:17 Comment(1)
This is already very useful! Do you know if there is an easy way to transfer definitions associated with a symbol between kernels? This is exactly what happens during parallel computations, and what DistributeDefinitions does. So I guess this is already implemented. But are the functions that do this user-accessible?Plash
D
10

This is on Windows, using Arnouds nice work and just adding plain old MathLink (pretty slow btw ...):

link = LinkCreate["8000", LinkProtocol -> "TCPIP"];
UsingFrontEnd[
NotebookPauseForEvaluation[nb_] := Module[{},
 While[ NotebookEvaluatingQ[nb], Pause[.25] ] ];
NotebookEvaluatingQ[nb_]:=Module[{},
 SelectionMove[nb,All,Notebook];
 Or@@Map["Evaluating"/.#&,Developer`CellInformation[nb]]
];
nb = NotebookOpen["G:\\mma\\test.nb"];
SelectionMove[nb, Before, Notebook];
NotebookWrite[nb, Cell["Link = LinkConnect[\"8000\", LinkProtocol -> \"TCPIP\"]", "Input"]];
SelectionMove[nb, After, Notebook];
NotebookWrite[nb, Cell["LinkWrite[Link, a]", "Input"]];
SelectionMove[nb, All, Notebook];
SelectionEvaluate[nb];
a = LinkRead[link];
Print["a = ",a];
]
Debi answered 28/12, 2011 at 18:40 Comment(4)
Thanks! Do we really need LinkProtocol -> "TCPIP" here or can we use the more efficient default? (I think by default it uses memory mapped files for interprocess commuincation)Plash
Probably. At least I cannot get it to work with LinkProtocol -> "SharedMemory"Debi
BTW: Why do you really need this at all? Is it not easier to save your results by DumpSave or some such?Debi
Perhaps he develops code on a laptop and wants to run it on remote machines. That is what I do and I find these answers very useful (and using a notebook rather than an m file is, in my case, so I can mix code and typeset discussion).Deficient

© 2022 - 2024 — McMap. All rights reserved.