Description
This function starts a short-lived new (``child'') Macaulay2 process, loads the file fname, runs the function func with the parameters params, captures the value returned by func, and stores it inside h in the original Macaulay2 process. Optionally, strict resource limits may be imposed on the child process from within Macaulay2, or data may be collected about the resources used by the child process.
Since the child is a new Macaulay2 process, it has no defined variables or functions except those defined in fname. Hence, func and anything it needs (e.g., ring definitions) must be defined in the file fname.
The hash table h stores the exit code of the created Macaulay2 process, the return code of the created Macaulay2 process (see run for details; this is usually 256 times the exit code, plus information about any signals received by the child), the wall-clock time used (as opposed to the CPU time), the name of the output file (unless it was deleted), the name of the answer file (unless it was deleted), any statistics recorded about the resource usage, and the value returned by the function func. If the child process terminates abnormally, then usually the exit code is nonzero and the value returned is null.
For example, we can write a few functions to a temporary file:
i1 : fn=temporaryFileName()|".m2"
o1 = /tmp/M2-880453-0/0.m2
|
i2 : fn<</// square = (x) -> (stderr<<"Running"<<endl; sleep(1); x^2); ///<<endl;
|
i3 : fn<</// justexit = () -> ( exit(27); ); ///<<endl;
|
i4 : fn<</// spin = (x) -> (stderr<<"Spinning!!"<<endl; startTime:=cpuTime(); while(cpuTime()-startTime<x) do for i to 10000000 do i; return(x);); ///<<endl;
|
i5 : fn<<flush;
|
i6 : h=runExternalM2(fn,"square",(4));
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/1.m2" >"/tmp/M2-880453-0/1.out" 2>&1 ))
Finished running.
|
i7 : h
o7 = HashTable{"answer file" => null}
"exit code" => 0
"output file" => null
"return code" => 0
"statistics" => null
"time used" => 2
value => 16
o7 : HashTable
|
i8 : h#value===4^2
o8 = true
|
i9 : h#"exit code"===0
o9 = true
|
An abnormal program exit will have a nonzero exit code; also, the value will be null, the output file should exist, but the answer file may not exist unless the routine finished successfully.
i10 : h=runExternalM2(fn,"justexit",());
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/2.m2" >"/tmp/M2-880453-0/2.out" 2>&1 ))
Finished running.
RunExternalM2: expected answer file does not exist
|
i11 : h
o11 = HashTable{"answer file" => /tmp/M2-880453-0/2.ans}
"exit code" => 27
"output file" => /tmp/M2-880453-0/2.out
"return code" => 6912
"statistics" => null
"time used" => 0
value => null
o11 : HashTable
|
i12 : fileExists(h#"output file")
o12 = true
|
i13 : fileExists(h#"answer file")
o13 = false
|
Here, we use resource limits to limit the routine to 2 seconds of computational time, while the system is asked to use 10 seconds of computational time:
i14 : h=runExternalM2(fn,"spin",10,PreRunScript=>"ulimit -t 2");
Running (ulimit -t 2 && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/3.m2" >"/tmp/M2-880453-0/3.out" 2>&1 ))
Killed
Finished running.
RunExternalM2: expected answer file does not exist
|
i15 : h
o15 = HashTable{"answer file" => /tmp/M2-880453-0/3.ans}
"exit code" => 0
"output file" => /tmp/M2-880453-0/3.out
"return code" => 9
"statistics" => null
"time used" => 1
value => null
o15 : HashTable
|
i16 : if h#"output file" =!= null and fileExists(h#"output file") then get(h#"output file")
o16 =
i1 : -- Script /tmp/M2-880453-0/3.m2 automatically generated by RunExternalM2
needsPackage("RunExternalM2",Configuration=>{"isChild"=>true});
i2 : load "/tmp/M2-880453-0/0.m2";
i3 : runExternalM2ReturnAnswer("/tmp/M2-880453-0/3.ans",spin (10));
Spinning!!
|
i17 : if h#"answer file" =!= null and fileExists(h#"answer file") then get(h#"answer file")
|
We can get quite a lot of detail on the resources used with the KeepStatistics command:
i18 : h=runExternalM2(fn,"spin",3,KeepStatistics=>true);
Running (true && ( (/usr/bin/time --verbose sh -c '/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/4.m2" >"/tmp/M2-880453-0/4.out" 2>&1') >"/tmp/M2-880453-0/4.stat" 2>&1 ))
Finished running.
|
i19 : h#"statistics"
o19 = Command being timed: "sh -c /usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/4.m2" >"/tmp/M2-880453-0/4.out" 2>&1"
User time (seconds): 5.86
System time (seconds): 0.35
Percent of CPU this job got: 171%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:03.62
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 101512
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 39230
Voluntary context switches: 10496
Involuntary context switches: 1127
Swaps: 0
File system inputs: 0
File system outputs: 16
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0
|
We can handle most kinds of objects as return values, although MutableMatrix does not work. Here, we use the built-in identity function:
i20 : v=/// A complicated string^%&C@#CERQVASDFQ#BQBSDH"' ewrjwklsf///;
|
i21 : (runExternalM2(fn,identity,v))#value===v
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/6.m2" >"/tmp/M2-880453-0/6.out" 2>&1 ))
Finished running.
o21 = true
|
Some care is required, however:
i22 : R=QQ[x,y];
|
i23 : v=coker random(R^2,R^{3:-1})
o23 = cokernel | 9/2x+1/2y x+3/4y 7/4x+7/9y |
| 9/4x+1/2y 3/2x+3/4y 7/10x+1/2y |
2
o23 : R-module, quotient of R
|
i24 : h=runExternalM2(fn,identity,v)
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/7.m2" >"/tmp/M2-880453-0/7.out" 2>&1 ))
Finished running.
RunExternalM2: expected answer file does not exist
o24 = HashTable{"answer file" => /tmp/M2-880453-0/7.ans}
"exit code" => 1
"output file" => /tmp/M2-880453-0/7.out
"return code" => 256
"statistics" => null
"time used" => 1
value => null
o24 : HashTable
|
To view the error message:
i25 : get(h#"output file")
o25 =
i1 : -- Script /tmp/M2-880453-0/7.m2 automatically generated by RunExternalM2
needsPackage("RunExternalM2",Configuration=>{"isChild"=>true});
i2 : load "/tmp/M2-880453-0/0.m2";
i3 : runExternalM2ReturnAnswer("/tmp/M2-880453-0/7.ans",identity (cokernel(map(R^2,R^{{-1}, {-1}, {-1}},{{(9/2)*x+(1/2)*y, x+(3/4)*y, (7/4)*x+(7/9)*y}, {(9/4)*x+(1/2)*y, (3/2)*x+(3/4)*y, (7/10)*x+(1/2)*y}}))));
stdio:4:76:(3):[1]: error: no method for binary operator ^ applied to objects:
-- R (of class Symbol)
-- ^ 2 (of class ZZ)
|
Keep in mind that the object you are passing must make sense in the context of the file containing your function! For instance, here we need to define the ring:
i26 : fn<<///R=QQ[x,y];///<<endl<<flush;
|
i27 : (runExternalM2(fn,identity,v))#value===v
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/8.m2" >"/tmp/M2-880453-0/8.out" 2>&1 ))
Finished running.
o27 = true
|
i28 : v=R;
|
i29 : h=runExternalM2(fn,identity,v);
Running (true && (/usr/local/bin/M2-binary --stop --no-debug --silent -q <"/tmp/M2-880453-0/9.m2" >"/tmp/M2-880453-0/9.out" 2>&1 ))
Finished running.
|
i30 : h#value
o30 = QQ[x..y]
o30 : PolynomialRing
|
i31 : v===h#value
o31 = false
|
i32 : R===value(toExternalString(R))
o32 = false
|