(self, func, *args, nprocs=None, **kwargs)
return self
Analyze sampled results using the given function in parallel.

        Parameters
        ----------
        func : function,
            Analysis method to use. The provided function must accept the
            problem specification as the first parameter, X values if needed,
            Y values, and return a numpy array.

        *args : list,
            Additional arguments to be passed to `func`

        nprocs : int,
            Number of processors to use.
            Capped to the number of outputs or available processors.

        **kwargs : dict,
            Additional keyword arguments passed to `func`

        Returns
        ----------
        self : ProblemSpec object
Analyze sampled results using the given function in parallel.
Analyze sampled results using the given function in parallel .
def analyze_parallel(self, func, *args, nprocs=None, **kwargs):
        """Analyze sampled results using the given function in parallel.

        Parameters
        ----------
        func : function,
            Analysis method to use. The provided function must accept the
            problem specification as the first parameter, X values if needed,
            Y values, and return a numpy array.

        *args : list,
            Additional arguments to be passed to `func`

        nprocs : int,
            Number of processors to use.
            Capped to the number of outputs or available processors.

        **kwargs : dict,
            Additional keyword arguments passed to `func`

        Returns
        ----------
        self : ProblemSpec object
        """
        warnings.warn("Parallel analysis is an experimental feature and may not work.")

        if self._results is None:
            raise RuntimeError("Model not yet evaluated")

        if 'X' in func.__code__.co_varnames:
            # enforce passing of X if expected
            func = partial(func, *args, X=self._samples, **kwargs)
        else:
            func = partial(func, *args, **kwargs)

        out_cols = self.get('outputs', None)
        if out_cols is None:
            if len(self._results.shape) == 1:
                self['outputs'] = ['Y']
            else:
                num_cols = self._results.shape[1]
                self['outputs'] = [f'Y{i}' for i in range(1, num_cols+1)]

        # Cap number of processors used
        Yn = len(self['outputs'])
        if Yn == 1:
            # Only single output, cannot parallelize
            warnings.warn(f"Analysis was not parallelized: {nprocs} processors requested for 1 output.")

            res = func(self, Y=self._results)
        else:
            max_procs = cpu_count()
            if nprocs is None:
                nprocs = max_procs
            else:
                nprocs = min(Yn, nprocs, max_procs)

            if ptqdm_available:
                # Display progress bar if available
                res = p_imap(lambda y: func(self, Y=y),
                             [self._results[:, i] for i in range(Yn)],
                             num_cpus=nprocs)
            else:
                with Pool(nprocs) as pool:
                    res = list(pool.imap(lambda y: func(self, Y=y),
                               [self._results[:, i] for i in range(Yn)]))

        # Assign by output name if more than 1 output, otherwise
        # attach directly
        if Yn > 1:
            self._analysis = {}
            for out, Si in zip(self['outputs'], list(res)):
                self._analysis[out] = Si
        else:
            self._analysis = res

        return self
def analyze_parallel ( self , func , * args , nprocs = None , * * kwargs ) : warnings . warn ( "Parallel analysis is an experimental feature and may not work." ) if self . _results is None : raise RuntimeError ( "Model not yet evaluated" ) if 'X' in func . __code__ . co_varnames : # enforce passing of X if expected func = partial ( func , * args , X = self . _samples , * * kwargs ) else : func = partial ( func , * args , * * kwargs ) out_cols = self . get ( 'outputs' , None ) if out_cols is None : if len ( self . _results . shape ) == 1 : self [ 'outputs' ] = [ 'Y' ] else : num_cols = self . _results . shape [ 1 ] self [ 'outputs' ] = [ f'Y{i}' for i in range ( 1 , num_cols + 1 ) ] # Cap number of processors used Yn = len ( self [ 'outputs' ] ) if Yn == 1 : # Only single output, cannot parallelize warnings . warn ( f"Analysis was not parallelized: {nprocs} processors requested for 1 output." ) res = func ( self , Y = self . _results ) else : max_procs = cpu_count ( ) if nprocs is None : nprocs = max_procs else : nprocs = min ( Yn , nprocs , max_procs ) if ptqdm_available : # Display progress bar if available res = p_imap ( lambda y : func ( self , Y = y ) , [ self . _results [ : , i ] for i in range ( Yn ) ] , num_cpus = nprocs ) else : with Pool ( nprocs ) as pool : res = list ( pool . imap ( lambda y : func ( self , Y = y ) , [ self . _results [ : , i ] for i in range ( Yn ) ] ) ) # Assign by output name if more than 1 output, otherwise # attach directly if Yn > 1 : self . _analysis = { } for out , Si in zip ( self [ 'outputs' ] , list ( res ) ) : self . _analysis [ out ] = Si else : self . _analysis = res return self