2%> Perform the basic runtime checks
for the sampler and
return nothing.
4%> \param[inout] self : The input/output parent
object of
class [pm.sampling.Sampler](@ref
Sampler)
5%>
which is **implicitly** passed to
this dynamic method (not by the user).<br>
6%> \param[in]
getLogFunc : The input MATLAB function handle or anonymous (lambda) function
7%> containing the implementation of the objective function to be sampled.<br>
8%> This user-specified function must have the following interface,<br>
16%> <li> the input argument ``state`` is a vector of type MATLAB ``
double``
17%> of size ``ndim`` representing a single point from within the ``ndim``
18%> dimensional domain of the mathematical
object function to be explored.<br>
19%> <li> the output argument `logFunc` is a scalar of the same type as the
20%> input ``state`` containing the natural logarithm of the objective
21%> function at the specified input ``state`` within its domain.<br>
23%> \param[in] ndim : The input scalar positive-valued whole-number representing the number of dimensions
24%> of the domain of the user-specified objective function in the input ``
getLogFunc()``.<br>
29%> sampler = pm.sampling.Sampler();
37%> \JoshuaOsborne, May 21 2024, 12:38 AM, University of Texas at Arlington<br>
38%> \FatemehBagheri, May 20 2024, 1:25 PM, NASA Goddard Space Flight Center (GSFC), Washington, D.C.<br>
39%> \AmirShahmoradi, May 16 2016, 9:03 AM, Oden Institute
for Computational Engineering and Sciences (ICES), UT Austin<br>
43 %%%% Sanitize ``sampler.silent``.
46 if ~pm.introspection.istype(self.silent,
"logical", 1)
47 help(
"pm.sampling.Sampler.silent");
48 disp(
"self.silent =");
51 +
"The sampler attribute ``silent`` must be a scalar of type ``logical``." + newline ...
52 +
"For more information, see the documentation displayed above." + newline ...
58 %%%% Sanitize parallelism method to set reporting permission.
62 % ismember(
'mpiname', who(
'global'));
63 %
if pm.array.len(self.mpiname) == 0 && ~isempty(mpiname) && pm.introspection.istype(mpiname,
"string", 1) % MPI enabled by a global definition of ``mpiname``.
64 % self.mpiname = mpiname;
67 if ~pm.introspection.istype(self.mpiname,
"string", 1)
70 %%%% Sanitize ``mpiname``.
73 help(
"pm.sampling.Sampler.mpiname");
77 +
"The sampler attribute ``mpiname`` must be a scalar of type ``char`` or ``string``." + newline ...
78 +
"For more information, see the documentation displayed above." + newline ...
82 elseif 0 == pm.array.len(self.mpiname)
85 %%%% Detect potential MPI launcher.
88 [mpiname,
nproc, ~] = pm.lib.mpi.runtime.detect();
89 if pm.array.len(mpiname) > 0 &&
nproc > 1
90 self.mpiname = mpiname;
96 %%%% First
detect potential use of MPI, then check
for thread parallelism.
99 if 0 < pm.array.len(self.mpiname) % MPI enabled.
101 self.silent =
true; % Otherwise, we keep the
default value of self.silent.
102 self.partype = string(pm.lib.mpi.name(self.mpiname));
104 elseif ~isempty(self.spec.parallelismNumThread)
107 %%%% Sanitize ``self.spec.parallelismNumThread``.
110 % The following separate conditions are crucial to remain separate.
111 failed = ~pm.introspection.istype(self.spec.parallelismNumThread,
"integer", 1);
113 failed = self.spec.parallelismNumThread < 0;
116 failed = ~pm.matlab.has.parallel;
119 self.partype =
"openmp";
120 if self.spec.parallelismNumThread == 0
121 %%%% Negative threads count indicates to the ParaMonte sampler that the user set `parallelismNumThread` to zero.
122 self.spec.parallelismNumThread = -maxNumCompThreads();
125 help(
"pm.sampling.Sampler.run");
127 +
"The simulation specification ``parallelismNumThread`` must be" + newline ...
128 +
"a scalar non-negative integer or whole-number representing the number" + newline ...
129 +
"of processor threads to use for a thread-parallel simulation." + newline ...
130 +
"Specifying ``0`` as the value of ``parallelismNumThread`` will" + newline ...
131 +
"lead to using all available CPU processes for thread-parallelism." + newline ...
132 +
"Ideally, a number less than the maximum available number" + newline ...
133 +
"of threads should be specified for ``parallelismNumThread`` as using" + newline ...
134 +
"all available threads exclusively for a simulation will" + newline ...
135 +
"slow down all open system applications including MATLAB." + newline ...
136 +
"Specifying this option requires the MATLAB parallel toolbox." + newline ...
137 +
"If missing or specified as empty `[]`, the simulation will run in serial mode." + newline ...
138 +
"You have specified:" + newline ...
140 + pm.io.tab +
"self.spec.parallelismNumThread = " +
string(self.parallelismNumThread) + newline ...
142 +
"Does your matlab have Parallel Computing Toolbox?" + newline ...
144 + pm.io.tab +
"pm.matlab.has.parallel() = " +
string(pm.matlab.has.parallel()) + newline ...
146 +
"For more information, see the documentation displayed above." + newline ...
158 help("pm.sampling.
Sampler.run");
160 + "The input argument
getLogFunc must be a callable function." + newline ...
161 + "It represents the user's objective function to be sampled," + newline ...
162 + "
which must take a single input argument of type numpy" + newline ...
163 + "float64 array of length ndim and must return the" + newline ...
164 + "natural logarithm of the objective function." + newline ...
166 + "For more information, see the documentation displayed above." + newline ...
172 %%%% Sanitize ``ndim``.
175 failed = ~pm.introspection.
istype(ndim, "integer", 1);
182 help("pm.sampling.
Sampler.run");
186 + "The input argument ``ndim`` must be a scalar positive integer (or whole-number)." + newline ...
187 + "The input argument ``ndim`` represents the number of dimensions of the domain" + newline ...
188 + "of the user-specified objective function ``
getLogFunc()``." + newline ...
189 + "For more information, see the documentation displayed above." + newline ...
195 %%%% Sanitize ``input`` specifications/file
string.
198 if ~pm.introspection.
istype(self.input, "
string", 1)
199 help("pm.sampling.
Sampler.input");
200 disp("self.input = ");
203 + "The specified sampler attribute ``input`` must be scalar MATLAB
string." + newline ...
204 + "For more information, see the documentation displayed above." + newline ...
208 self.input =
string(self.input);
210 if isfile(self.input)
211 self.nml = pm.sys.path.
abs(self.input, "lean");
213 self.nml = self.input; % Could still be a valid namelist.
216 warning ( newline ...
217 + "User-specified input namelist file detected: " + newline ...
219 + pm.io.
tab + """" + self.input + """" + newline ...
221 + "All simulation specifications will be read from the input file." + newline ...
222 + "All simulation specifications in the ``spec`` component of the sampler
object will be ignored." + newline ...
227 %self.nml = [convertStringsToChars(self.getInputFile())];
228 self.nml = "&" + self.method + " " + self.spec.getEntriesNML(ndim) + " " + "/" + newline;
233 %%%% Sanitize ``checked``.
236 if ~isempty(self.checked)
237 if ~pm.introspection.
istype(self.checked, "logical", 1)
239 disp("self.checked =");
242 + "The specified sampler attribute ``checked`` must be a scalar MATLAB logical." + newline ...
243 + "For more information, see the documentation displayed above." + newline ...
247 chktypes = "nocheck";
249 chktypes = "checked";
252 chktypes = ["nocheck", "checked"];
256 %%%% Setup the ParaMonter sampler library
name.
259 libspecs = [pm.os.
namel(), pm.sys.
arch(), self.libtype, self.memtype, self.partype];
260 mexdirs = pm.
lib.path.
mexdir(self.mexname, libspecs);
262 % We will choose the checking mode,
compiler suite,
263 % and build mode based on the availability below.
265 if self.bldtype == ""
270 if self.clstype == ""
275 failed = isempty(mexdirs);
278 for ichk = 1 : length(chktypes)
279 chktype = filesep + chktypes(ichk);
283 bldtype = filesep +
bldtypes(ibld) + filesep;
284 for imex = 1 : length(mexdirs)
285 if contains(mexdirs(imex), clstype) ...
286 && contains(mexdirs(imex), bldtype) ...
287 && contains(mexdirs(imex), chktype)
289 if self.bldtype == ""
292 if self.clstype == ""
319 + "There are no MEX libraries associated with the configurations displayed above:" + newline ...
320 + "Either the user has compromised internal structure of the ParaMonte library" + newline ...
321 + "or the user has tempered with hidden attributes of the ParaMonte sampler." + newline ...
322 + "If you believe neither is the case, please report this error at:" + newline ...
324 + pm.io.
tab + pm.web.
href(self.
weblinks.github.issues.url) + newline ...
326 + "for a quick resolution." + newline ...
332 %%%% Add the identified MEX path to the MATLAB pat
list, only temporarily.
337 path(self.matpath,
mexdir);
338 munlock(self.mexname);
341 if ~self.silent && ~(pm.matlab.
iscmd || pm.os.is.
win)
343 + "Also check the shell terminal (from
which you opened MATLAB)" + newline ...
344 + "for potential realtime simulation progress and report." + newline ...
349 if self.clstype == "gnu"
350 setenv('GFORTRAN_STDIN_UNIT' , '5');
351 setenv('GFORTRAN_STDOUT_UNIT', '6');
352 setenv('GFORTRAN_STDERR_UNIT', '0');
356 %%%% Set up the MEX file to call.
359 if self.partype == "openmp"
360 mexcall =
string(self.mexname + "(convertStringsToChars(self.method), @getLogFuncConcurrent, ndim, convertStringsToChars(self.nml))");
362 delete(gcp("nocreate"));
363 % The following works only in MATLAB 2022b and beyond.
364 if pm.matlab.
release() < "2022b"
365 pool = parpool("threads");
366 maxNumCompThreads(
abs(self.spec.parallelismNumThread));
368 pool = parpool("threads",
abs(self.spec.parallelismNumThread));
371 evalc('delete(gcp("nocreate"))');
372 if pm.matlab.
release() < "2022b"
373 evalc('pool = parpool("threads")');
374 evalc('maxNumCompThreads(
abs(self.spec.parallelismNumThread))');
376 evalc('pool = parpool("threads",
abs(self.spec.parallelismNumThread))');
380 mexcall =
string(self.mexname + "(convertStringsToChars(self.method), @getLogFuncWrapped, ndim, convertStringsToChars(self.nml))");
385 %%%% Define the ``
getLogFunc`` wrapper function for serial/MPI sampling.
388 function logFunc = getLogFuncWrapped(state)
393 %%%% Define the ``
getLogFunc`` wrapper function for OpenMP sampling.
397 function [logFunc, avgTimePerFunCall, avgCommPerFunCall] = getLogFuncConcurrent(state)
398 %state =
parallel.pool.Constant(state(2 : end, :));
399 avgCommPerFunCall = tic();
400 avgTimePerFunCall = 0;
401 njob = size(state, 2);
403 % %getState = arrayfun(@(ijob) state(:, ijob), 1 : njob, 'UniformOutput', 0);
404 % fout(1 : njob) =
parallel.FevalFuture;
405 % start = zeros(njob, 'uint64');
406 % delta = zeros(njob);
407 % for ijob = 1 : njob
408 % %slice = getState(ijob);
409 % start(ijob) = tic();
410 % %fout(ijob) = parfeval(pool,
getLogFunc, 1, slice{1});
411 % %fout(ijob) = parfeval(
getLogFunc, 1, state(:, ijob));
412 % fout(ijob) = parfeval(pool,
getLogFunc, 1, state(:, ijob));
413 % delta(ijob) = toc(start(ijob));
415 % avgTimePerFunCall = sum(delta);
416 % logFunc = fetchOutputs(fout);
420 % avgTimePerFunCall = tic();
422 % avgTimePerFunCall = toc(avgTimePerFunCall);
424 %logFunc = [logFunc{:}];
425 %avgTimePerFunCall = mean([avgTimePerFunCall{:}]);
430 % `parfor` is slightly slower than `parfeval` (abour 20 ms
for density function costing .1 ms).
431 % However, the slight penalty diminishes
for costly problems and furthermore, `parfor` allows
432 % a reliable method of timing the density function, unlike `parfeval`.
434 logFunc = zeros(njob, 1);
435 %getSlice = arrayfun(@(ijob) state(:, ijob), 1 : njob,
'UniformOutput', 0);
436 parfor ijob = 1 : njob
440 logFunc(ijob) = feval(
getLogFunc, state(:, ijob));
441 avgTimePerFunCall = avgTimePerFunCall + toc(start);
444 avgTimePerFunCall = avgTimePerFunCall / njob;
445 avgCommPerFunCall = toc(avgCommPerFunCall) - avgTimePerFunCall;
449 %%%% Call the MEX sampler.
457 + self.getppm() + newline ...
458 +
"For more information and examples on the usage, visit:" + newline ...
460 + pm.io.tab + pm.web.href(self.weblinks.docs.url) + newline ...
466 msg = string(me.identifier) +
" : " + string(me.message) + newline;
467 if ismac && strcmpi(me.identifier,
'MATLAB:mex:ErrInvalidMEXFile')
469 + "This error is most likely due to the ""System Integrity Protection""" + newline ...
470 + "(SIP) of your macOS interfering with the ParaMonte MATLAB MEX files." + newline ...
471 + "You can follow the guidelines in the documentation to resolve this error:" + newline ...
473 + pm.io.
tab + pm.web.
href(self.
weblinks.docs.url + "/notes/troubleshooting/macos-developer-cannot-be-verified/") + newline ...
475 + "If the problem persists even after following the guidelines" + newline ...
476 + "in the above page, please report this issue to the developers at:" + newline ...
483 + "The " + self.method + " simulation failed. See the error message above." + newline ...
484 + "Also check the contents of the generated output '*_report.txt' files" + newline ...
485 + "if any such files were generated before the simulation crash:" + newline ...
487 + pm.io.
tab + self.spec.outputFileName + "*_report.txt" + newline ...
function name(in vendor)
Return the MPI library name as used in naming the ParaMonte MATLAB shared library directories.
function list()
Return a list of MATLAB strings containing the names of OS platforms supported by the ParaMonte MATLA...
function abs(in path, in style)
Return the Get absolute canonical path of a file or folder.
function arch()
Return the processor architecture name of the current system on which MATLAB is running,...
function bldtypes()
Return a list of MATLAB strings containing the names of all currently possible builds of the ParaMont...
This is the base class for the ParaMonte sampler routines.
function clean()
Remove all paths that contain the ParaMonte lib directory from the MATLAB path variable.
function clstypes()
Return a list of MATLAB strings containing the names of all supported compiler suites (vendor names) ...
function compiler()
Return a scalar MATLAB logical that is true if and only if the current installation of MATLAB contain...
function detect(in vendor)
Return the MPI image count and current image ID (e.g., MPI rank + 1) and the MPI library name as it a...
function getLogFunc(in point)
function href(in url)
Return an HTML-style decoration of the input URL if the ParaMonte MATLAB library is used in GUI,...
function iscmd()
Return a scalar MATLAB logical true if and only if the the MATLAB binary is being called from the she...
function istype(in varval, in vartype, in varsize)
Return true if and only if the input varval conforms with the specified input type vartype and the sp...
function lib()
Return a scalar MATLAB string containing the path to the lib directory of the ParaMonte library packa...
function mexdir(in mexname, in config)
Return the vector of MATLAB strings containing the directory path(s) containing the specified ParaMon...
function namel()
Return a MATLAB string containing the lower-case name of the current OS.
function nproc(in vendor)
Return the runtime number of MPI processes with which the mpiexec launcher may have been invoked.
function parallel()
Return a scalar MATLAB logical that is true if and only if the current installation of MATLAB contain...
function release(in type)
Return a scalar MATLAB string containing the MATLAB release version, year, or season as requested.
function tab()
Return a scalar MATLAB string containing 4 blank characters equivalent to a tab character.
function weblinks()
Return a structure containing tree of weblinks for the ParaMonte MATLAB library source file and docum...
function which(in vendor)
Return the a MATLAB string containing the path to the first mpiexec executable binary found in system...
function win()
Return true if the current OS is Windows.