From 6f7aa95da3f6c7a6b45d5883c65094fc64d7da54 Mon Sep 17 00:00:00 2001 From: pr0m1th3as Date: Thu, 15 Feb 2024 05:57:46 +0200 Subject: [PATCH] tLocationScaleDistribution: new classdef for location-scale T probabiility distribution object --- inst/dist_obj/tLocationScaleDistribution.m | 815 +++++++++++++++++++++ 1 file changed, 815 insertions(+) create mode 100755 inst/dist_obj/tLocationScaleDistribution.m diff --git a/inst/dist_obj/tLocationScaleDistribution.m b/inst/dist_obj/tLocationScaleDistribution.m new file mode 100755 index 00000000..2c94bc84 --- /dev/null +++ b/inst/dist_obj/tLocationScaleDistribution.m @@ -0,0 +1,815 @@ +classdef tLocationScaleDistribution + ## -*- texinfo -*- + ## @deftypefn {statistics} tLocationScaleDistribution + ## + ## Weibull probability distribution object. + ## + ## A @code{tLocationScaleDistribution} object consists of parameters, a model + ## description, and sample data for a Weibull probability distribution. + ## + ## The Weibull distribution uses the following parameters. + ## + ## @multitable @columnfractions 0.25 0.48 0.27 + ## @headitem @var{Parameter} @tab @var{Description} @tab @var{Support} + ## + ## @item @qcode{mu} @tab Location parameter @tab @math{-Inf < mu < Inf} + ## @item @qcode{sigma} @tab Scale parameter @tab @math{sigma > 0} + ## @item @qcode{df} @tab Scale parameter @tab @math{df > 0} + ## @end multitable + ## + ## There are several ways to create a @code{tLocationScaleDistribution} object. + ## + ## @itemize + ## @item Fit a distribution to data using the @code{fitdist} function. + ## @item Create a distribution with specified parameter values using the + ## @code{makedist} function. + ## @item Use the constructor @qcode{tLocationScaleDistribution (@var{lambda}, + ## @var{mu})} to create a Weibull distribution with specified parameter values. + ## @item Use the static method @qcode{tLocationScaleDistribution.fit (@var{x}, + ## @var{censor}, @var{freq}, @var{options})} to a distribution to data @var{x}. + ## @end itemize + ## + ## It is highly recommended to use @code{fitdist} and @code{makedist} + ## functions to create probability distribution objects, instead of the + ## constructor and the aforementioned static method. + ## + ## A @code{tLocationScaleDistribution} object contains the following + ## properties, which can be accessed using dot notation. + ## + ## @multitable @columnfractions 0.25 0.25 0.25 0.25 + ## @item @qcode{DistributionName} @tab @qcode{DistributionCode} @tab + ## @qcode{CensoringAllowed} @tab @qcode{NumParameters} + ## @item @qcode{ParameterNames} @tab @qcode{ParameterRange} @tab + ## @qcode{ParameterLogCI} @tab @qcode{ParameterDescription} + ## @item @qcode{ParameterValues} @tab @qcode{ParameterCI} @tab + ## @qcode{NegLogLikelihood} @tab @qcode{ParameterCovariance} + ## @item @qcode{ParameterIsFixed} @tab @qcode{Truncation} @tab + ## @qcode{IsTruncated} @tab @qcode{InputData} + ## @end multitable + ## + ## A @code{tLocationScaleDistribution} object contains the following methods: + ## @code{cdf}, @code{icdf}, @code{iqr}, @code{mean}, @code{median}, + ## @code{negloglik}, @code{paramci}, @code{pdf}, @code{plot}, @code{proflik}, + ## @code{random}, @code{std}, @code{truncate}, @code{var}. + ## + ## Further information about the location-scale Student's T distribution can be + ## found at @url{https://en.wikipedia.org/wiki/Student%27s_t-distribution#Location-scale_t_distribution} + ## + ## @seealso{fitdist, makedist} + ## @end deftypefn + + properties (Dependent = true) + mu + sigma + df + endproperties + + properties (GetAccess = public, Constant = true) + DistributionName = "tLocationScaleDistribution"; + DistributionCode = "tls"; + CensoringAllowed = true; + NumParameters = 3; + ParameterNames = {"mu", "sigma", "df"}; + ParameterRange = [-Inf, realmin, realmin; Inf, Inf, Inf]; + ParameterLogCI = [true, true, true]; + ParameterDescription = {"Location", "Scale", "Degrees of Freedom"}; + endproperties + + properties (GetAccess = public , SetAccess = protected) + ParameterValues + ParameterCI + NegLogLikelihood + ParameterCovariance + ParameterIsFixed + Truncation + IsTruncated + InputData + endproperties + + methods (Hidden) + + function this = tLocationScaleDistribution (mu, sigma, df) + if (nargin == 0) + mu = 0; + sigma = 1; + df = 5; + endif + checkparams (mu, sigma, df) + this.InputData = []; + this.IsTruncated = false; + this.NegLogLikelihood = []; + this.ParameterValues = [mu, sigma, df]; + this.ParameterIsFixed = [true, true, true]; + this.ParameterCovariance = zeros (this.NumParameters); + endfunction + + function display (this) + fprintf ("%s =\n", inputname(1)); + __disp__ (this, "t Location-Scale distribution"); + endfunction + + function disp (this) + __disp__ (this, "t Location-Scale distribution"); + endfunction + + function this = set.mu (this, mu) + checkparams (mu, this.sigma, this.df) + this.InputData = []; + this.NegLogLikelihood = []; + this.ParameterValues(1) = mu; + this.ParameterIsFixed = [true, true, true]; + this.ParameterCovariance = zeros (this.NumParameters); + endfunction + + function mu = get.mu (this) + mu = this.ParameterValues(1); + endfunction + + function this = set.sigma (this, sigma) + checkparams (this.mu, sigma, this.df) + this.InputData = []; + this.NegLogLikelihood = []; + this.ParameterValues(2) = sigma; + this.ParameterCovariance = zeros (this.NumParameters); + this.InputData = []; + endfunction + + function sigma = get.sigma (this) + sigma = this.ParameterValues(2); + endfunction + + function this = set.df (this, df) + checkparams (this.mu, this.sigma, df) + this.InputData = []; + this.NegLogLikelihood = []; + this.ParameterValues(3) = df; + this.ParameterCovariance = zeros (this.NumParameters); + this.InputData = []; + endfunction + + function df = get.df (this) + df = this.ParameterValues(3); + endfunction + + endmethods + + methods (Access = public) + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{p} =} cdf (@var{pd}, @var{x}) + ## @deftypefnx {tLocationScaleDistribution} {@var{p} =} cdf (@var{pd}, @var{x}, @qcode{"upper"}) + ## + ## Compute the cumulative distribution function (CDF). + ## + ## @code{@var{p} = cdf (@var{pd}, @var{x})} computes the CDF of the + ## probability distribution object, @var{pd}, evaluated at the values in + ## @var{x}. + ## + ## @code{@var{p} = cdf (@dots{}, @qcode{"upper"})} returns the complement of + ## the CDF of the probability distribution object, @var{pd}, evaluated at + ## the values in @var{x}. + ## + ## @end deftypefn + function p = cdf (this, x, uflag) + if (! isscalar (this)) + error ("cdf: requires a scalar probability distribution."); + endif + ## Check for "upper" flag + if (nargin > 2 && strcmpi (uflag, "upper")) + utail = true; + elseif (nargin > 2 && ! strcmpi (uflag, "upper")) + error ("cdf: invalid argument for upper tail."); + else + utail = false; + endif + ## Do the computations + p = tlscdf (x, this.mu, this.sigma, this.df); + if (this.IsTruncated) + lx = this.Truncation(1); + lb = x < lx; + ux = this.Truncation(2); + ub = x > ux; + p(lb) = 0; + p(ub) = 1; + p(! (lb | ub)) -= tlscdf (lx, this.mu, this.sigma, this.df); + p(! (lb | ub)) /= diff (tlscdf ([lx, ux], this.mu, this.sigma, this.df)); + endif + ## Apply uflag + if (utail) + p = 1 - p; + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{p} =} icdf (@var{pd}, @var{p}) + ## + ## Compute the cumulative distribution function (CDF). + ## + ## @code{@var{p} = icdf (@var{pd}, @var{x})} computes the quantile (the + ## inverse of the CDF) of the probability distribution object, @var{pd}, + ## evaluated at the values in @var{x}. + ## + ## @end deftypefn + function x = icdf (this, p) + if (! isscalar (this)) + error ("icdf: requires a scalar probability distribution."); + endif + if (this.IsTruncated) + lp = tlscdf (this.Truncation(1), this.mu, this.sigma, this.df); + up = tlscdf (this.Truncation(2), this.mu, this.sigma, this.df); + ## Adjust p values within range of p @ lower limit and p @ upper limit + np = lp + (up - lp) .* p; + x = tlsinv (np, this.mu, this.sigma, this.df); + else + x = tlsinv (p, this.mu, this.sigma, this.df); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{r} =} iqr (@var{pd}) + ## + ## Compute the interquartile range of a probability distribution. + ## + ## @code{@var{r} = iqr (@var{pd})} computes the interquartile range of the + ## probability distribution object, @var{pd}. + ## + ## @end deftypefn + function r = iqr (this) + if (! isscalar (this)) + error ("iqr: requires a scalar probability distribution."); + endif + r = diff (icdf (this, [0.25, 0.75])); + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{m} =} mean (@var{pd}) + ## + ## Compute the mean of a probability distribution. + ## + ## @code{@var{m} = mean (@var{pd})} computes the mean of the probability + ## distribution object, @var{pd}. + ## + ## @end deftypefn + function m = mean (this) + if (! isscalar (this)) + error ("mean: requires a scalar probability distribution."); + endif + if (this.IsTruncated) + fm = @(x) x .* pdf (this, x); + m = integral (fm, this.Truncation(1), this.Truncation(2)); + else + m = tlsstat (this.mu, this.sigma, this.df); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{m} =} median (@var{pd}) + ## + ## Compute the median of a probability distribution. + ## + ## @code{@var{m} = median (@var{pd})} computes the median of the probability + ## distribution object, @var{pd}. + ## + ## @end deftypefn + function m = median (this) + if (! isscalar (this)) + error ("median: requires a scalar probability distribution."); + endif + if (this.IsTruncated) + lx = this.Truncation(1); + ux = this.Truncation(2); + Fa_b = wblcdf ([lx, ux], this.mu, this.sigma, this.df); + m = tlsinv (sum (Fa_b) / 2, this.mu, this.sigma, this.df); + else + m = tlsstat (this.mu, this.sigma, this.df); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{nlogL} =} negloglik (@var{pd}) + ## + ## Compute the negative loglikelihood of a probability distribution. + ## + ## @code{@var{m} = negloglik (@var{pd})} computes the negative loglikelihood + ## of the probability distribution object, @var{pd}. + ## + ## @end deftypefn + function nlogL = negloglik (this) + if (! isscalar (this)) + error ("negloglik: requires a scalar probability distribution."); + endif + if (isempty (this.InputData)) + nlogL = []; + return + endif + nlogL = - tlslike ([this.mu, this.sigma, this.df], this.InputData.data, ... + this.InputData.cens, this.InputData.freq); + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{ci} =} paramci (@var{pd}) + ## @deftypefnx {tLocationScaleDistribution} {@var{ci} =} paramci (@var{pd}, @var{Name}, @var{Value}) + ## + ## Compute the confidence intervals for probability distribution parameters. + ## + ## @code{@var{ci} = paramci (@var{pd})} computes the lower and upper + ## boundaries of the 95% confidence interval for each parameter of the + ## probability distribution object, @var{pd}. + ## + ## @code{@var{ci} = paramci (@var{pd}, @var{Name}, @var{Value})} computes the + ## confidence intervals with additional options specified specified by + ## @qcode{Name-Value} pair arguments listed below. + ## + ## @multitable @columnfractions 0.18 0.02 0.8 + ## @headitem @var{Name} @tab @tab @var{Value} + ## + ## @item @qcode{"Alpha"} @tab @tab A scalar value in the range @math{(0,1)} + ## specifying the significance level for the confidence interval. The + ## default value 0.05 corresponds to a 95% confidence interval. + ## + ## @item @qcode{"Parameter"} @tab @tab A character vector or a cell array of + ## character vectors specifying the parameter names for which to compute + ## confidence intervals. By default, @code{paramci} computes confidence + ## intervals for all distribution parameters. + ## @end multitable + ## + ## @code{paramci} is meaningful only when @var{pd} is fitted to data, + ## otherwise an empty array, @qcode{[]}, is returned. + ## + ## @end deftypefn + function ci = paramci (this, varargin) + if (! isscalar (this)) + error ("paramci: requires a scalar probability distribution."); + endif + if (isempty (this.InputData)) + ci = [this.ParameterValues; this.ParameterValues]; + else + ci = __paramci__ (this, varargin{:}); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{y} =} pdf (@var{pd}, @var{x}) + ## + ## Compute the probability distribution function (PDF). + ## + ## @code{@var{y} = pdf (@var{pd}, @var{x})} computes the PDF of the + ## probability distribution object, @var{pd}, evaluated at the values in + ## @var{x}. + ## + ## @end deftypefn + function y = pdf (this, x) + if (! isscalar (this)) + error ("pdf: requires a scalar probability distribution."); + endif + y = tlspdf (x, this.mu, this.sigma, this.df); + if (this.IsTruncated) + lx = this.Truncation(1); + lb = x < lx; + ux = this.Truncation(2); + ub = x > ux; + y(lb | ub) = 0; + y(! (lb | ub)) /= diff (tlscdf ([lx, ux], this.mu, this.sigma, this.df)); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {} plot (@var{pd}) + ## @deftypefnx {tLocationScaleDistribution} {} plot (@var{pd}, @var{Name}, @var{Value}) + ## @deftypefnx {tLocationScaleDistribution} {@var{h} =} plot (@dots{}) + ## + ## Plot a probability distribution object. + ## + ## @code{plot (@var{pd}} plots a probability density function (PDF) of the + ## probability distribution object @var{pd}. If @var{pd} contains data, + ## which have been fitted by @code{fitdist}, the PDF is superimposed over a + ## histogram of the data. + ## + ## @code{plot (@var{pd}, @var{Name}, @var{Value})} specifies additional + ## options with the @qcode{Name-Value} pair arguments listed below. + ## + ## @multitable @columnfractions 0.18 0.02 0.8 + ## @headitem @tab @var{Name} @tab @var{Value} + ## + ## @item @qcode{"PlotType"} @tab @tab A character vector specifying the plot + ## type. @qcode{"pdf"} plots the probability density function (PDF). When + ## @var{pd} is fit to data, the PDF is superimposed on a histogram of the + ## data. @qcode{"cdf"} plots the cumulative density function (CDF). When + ## @var{pd} is fit to data, the CDF is superimposed over an empirical CDF. + ## @qcode{"probability"} plots a probability plot using a CDF of the data + ## and a CDF of the fitted probability distribution. This option is + ## available only when @var{pd} is fitted to data. + ## + ## @item @qcode{"Discrete"} @tab @tab A logical scalar to specify whether to + ## plot the PDF or CDF of a discrete distribution object as a line plot or a + ## stem plot, by specifying @qcode{false} or @qcode{true}, respectively. By + ## default, it is @qcode{true} for discrete distributions and @qcode{false} + ## for continuous distributions. When @var{pd} is a continuous distribution + ## object, option is ignored. + ## + ## @item @qcode{"Parent"} @tab @tab An axes graphics object for plot. If + ## not specified, the @code{plot} function plots into the current axes or + ## creates a new axes object if one does not exist. + ## @end multitable + ## + ## @code{@var{h} = plot (@dots{})} returns a graphics handle to the plotted + ## objects. + ## + ## @end deftypefn + function [varargout] = plot (this, varargin) + if (! isscalar (this)) + error ("plot: requires a scalar probability distribution."); + endif + h = __plot__ (this, false, varargin{:}); + if (nargout > 0) + varargout{1} = h; + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}) + ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @qcode{"Display"}, @var{display}) + ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}) + ## @deftypefnx {tLocationScaleDistribution} {[@var{nlogL}, @var{param}] =} proflik (@var{pd}, @var{pnum}, @var{setparam}, @qcode{"Display"}, @var{display}) + ## + ## Profile likelihood function for a probability distribution object. + ## + ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum})} + ## returns a vector @var{nlogL} of negative loglikelihood values and a + ## vector @var{param} of corresponding parameter values for the parameter in + ## the position indicated by @var{pnum}. By default, @code{proflik} uses + ## the lower and upper bounds of the 95% confidence interval and computes + ## 100 equispaced values for the selected parameter. @var{pd} must be + ## fitted to data. + ## + ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, + ## @qcode{"Display"}, @qcode{"on"})} also plots the profile likelihood + ## against the default range of the selected parameter. + ## + ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, + ## @var{setparam})} defines a user-defined range of the selected parameter. + ## + ## @code{[@var{nlogL}, @var{param}] = proflik (@var{pd}, @var{pnum}, + ## @var{setparam}, @qcode{"Display"}, @qcode{"on"})} also plots the profile + ## likelihood against the user-defined range of the selected parameter. + ## + ## For the location-scale T distribution, @qcode{@var{pnum} = 1} selects the + ## parameter @qcode{mu}, @qcode{@var{pnum} = 2} selects the parameter + ## @qcode{sigma}, and @qcode{@var{pnum} = 3} selects the parameter @var{df}. + ## + ## When opted to display the profile likelihood plot, @code{proflik} also + ## plots the baseline loglikelihood computed at the lower bound of the 95% + ## confidence interval and estimated maximum likelihood. The latter might + ## not be observable if it is outside of the used-defined range of parameter + ## values. + ## + ## @end deftypefn + function [varargout] = proflik (this, pnum, varargin) + if (! isscalar (this)) + error ("proflik: requires a scalar probability distribution."); + endif + if (isempty (this.InputData)) + error ("proflik: no fitted data available."); + endif + [varargout{1:nargout}] = __proflik__ (this, pnum, varargin{:}); + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{y} =} random (@var{pd}) + ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, @var{rows}) + ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, @var{rows}, @var{cols}, @dots{}) + ## @deftypefnx {tLocationScaleDistribution} {@var{y} =} random (@var{pd}, [@var{sz}]) + ## + ## Generate random arrays from the probability distribution object. + ## + ## @code{@var{r} = random (@var{pd})} returns a random number from the + ## distribution object @var{pd}. + ## + ## When called with a single size argument, @code{betarnd} returns a square + ## matrix with the dimension specified. When called with more than one + ## scalar argument, the first two arguments are taken as the number of rows + ## and columns and any further arguments specify additional matrix + ## dimensions. The size may also be specified with a row vector of + ## dimensions, @var{sz}. + ## + ## @end deftypefn + function r = random (this, varargin) + if (! isscalar (this)) + error ("random: requires a scalar probability distribution."); + endif + if (this.IsTruncated) + sz = [varargin{:}]; + ps = prod (sz); + ## Get an estimate of how many more random numbers we need to randomly + ## pick the appropriate size from + lx = this.Truncation(1); + ux = this.Truncation(2); + ratio = 1 / diff (ricecdf ([ux, lx], this.nu, this.sigma)); + nsize = 2 * ratio * ps; # times 2 to be on the safe side + ## Generate the numbers and remove out-of-bound random samples + r = tlsrnd (this.mu, this.sigma, this.df, nsize, 1); + r(r < lx | r > ux) = []; + ## Randomly select the required size and reshape to requested dimensions + r = randperm (r, ps); + r = reshape (r, sz); + else + r = tlsrnd (this.mu, this.sigma, this.df, varargin{:}); + endif + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{s} =} std (@var{pd}) + ## + ## Compute the standard deviation of a probability distribution. + ## + ## @code{@var{s} = std (@var{pd})} computes the standard deviation of the + ## probability distribution object, @var{pd}. + ## + ## @end deftypefn + function s = std (this) + if (! isscalar (this)) + error ("std: requires a scalar probability distribution."); + endif + v = var (this.mu, this.sigma, this.df); + s = sqrt (v); + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{t} =} truncate (@var{pd}, @var{lower}, @var{upper}) + ## + ## Truncate a probability distribution. + ## + ## @code{@var{t} = truncate (@var{pd})} returns a probability distribution + ## @var{t}, which is the probability distribution @var{pd} truncated to the + ## specified interval with lower limit, @var{lower}, and upper limit, + ## @var{upper}. If @var{pd} is fitted to data with @code{fitdist}, the + ## returned probability distribution @var{t} is not fitted, does not contain + ## any data or estimated values, and it is as it has been created with the + ## @var{makedist} function, but it includes the truncation interval. + ## + ## @end deftypefn + function this = truncate (this, lower, upper) + if (! isscalar (this)) + error ("truncate: requires a scalar probability distribution."); + endif + if (nargin < 3) + error ("truncate: missing input argument."); + elseif (lower >= upper) + error ("truncate: invalid lower upper limits."); + endif + this.Truncation = [lower, upper]; + this.IsTruncated = true; + this.InputData = []; + this.NegLogLikelihood = []; + this.ParameterIsFixed = [true, true]; + this.ParameterCovariance = zeros (this.NumParameters); + endfunction + + ## -*- texinfo -*- + ## @deftypefn {tLocationScaleDistribution} {@var{v} =} var (@var{pd}) + ## + ## Compute the variance of a probability distribution. + ## + ## @code{@var{v} = var (@var{pd})} computes the standard deviation of the + ## probability distribution object, @var{pd}. + ## + ## @end deftypefn + function v = var (this) + if (! isscalar (this)) + error ("var: requires a scalar probability distribution."); + endif + if (this.IsTruncated) + fm = @(x) x .* pdf (this, x); + mu = integral (fm, this.Truncation(1), this.Truncation(2)); + fv = @(x) ((x - mu) .^ 2) .* pdf (pd, x); + v = integral (fv, this.Truncation(1), this.Truncation(2)); + else + [~, v] = wblstat (this.lambda, this.mu); + endif + endfunction + + endmethods + + methods (Static, Hidden) + + function pd = fit(x, varargin) + ## Check input arguments + if (nargin < 2) + censor = []; + endif + if (nargin < 3) + freq = []; + endif + if (nargin < 4) + options.Display = "off"; + options.TolX = 1e-6; + endif + ## Fit data + [phat, pci] = tlsfit (x, 0.05, censor, freq, options); + [nlogL, acov] = tlslike (phat, x, censor, freq); + ## Create fitted distribution object + pd = tLocationScaleDistribution.makeFitted ... + (phat, pci, nlogL, acov, x, censor, freq); + endfunction + + function pd = makeFitted (phat, pci, nlogL, acov, x, censor, freq) + mu = phat(1); + sigma = phat(2); + df = phat(2); + pd = tLocationScaleDistribution (mu, sigma, df); + pd.ParameterCI = pci; + pd.NegLogLikelihood = nlogL; + pd.ParameterIsFixed = [false, false]; + pd.ParameterCovariance = acov; + pd.InputData = struct ("data", x, "cens", censor, "freq", freq); + endfunction + + endmethods + +endclassdef + +function checkparams (mu, sigma, df) + if (! (isscalar (mu) && isnumeric (mu) && isreal (mu) && isfinite (mu))) + error ("tLocationScaleDistribution: MU must be a real scalar.") + endif + if (! (isscalar (sigma) && isnumeric (sigma) && isreal (sigma) + && isfinite (sigma) && sigma > 0)) + error ("tLocationScaleDistribution: SIGMA must be a positive real scalar.") + endif + if (! (isscalar (df) && isnumeric (df) && isreal (df) + && isfinite (df) && df > 0)) + error ("tLocationScaleDistribution: DF must be a positive real scalar.") + endif +endfunction + +## Test input validation +## 'tLocationScaleDistribution' constructor +%!error ... +%! tLocationScaleDistribution(i, 1, 1) +%!error ... +%! tLocationScaleDistribution(Inf, 1, 1) +%!error ... +%! tLocationScaleDistribution([1, 2], 1, 1) +%!error ... +%! tLocationScaleDistribution("a", 1, 1) +%!error ... +%! tLocationScaleDistribution(NaN, 1, 1) +%!error ... +%! tLocationScaleDistribution(0, 0, 1) +%!error ... +%! tLocationScaleDistribution(0, -1, 1) +%!error ... +%! tLocationScaleDistribution(0, Inf, 1) +%!error ... +%! tLocationScaleDistribution(0, i, 1) +%!error ... +%! tLocationScaleDistribution(0, "a", 1) +%!error ... +%! tLocationScaleDistribution(0, [1, 2], 1) +%!error ... +%! tLocationScaleDistribution(0, NaN, 1) +%!error ... +%! tLocationScaleDistribution(0, 1, 0) +%!error ... +%! tLocationScaleDistribution(0, 1, -1) +%!error ... +%! tLocationScaleDistribution(0, 1, Inf) +%!error ... +%! tLocationScaleDistribution(0, 1, i) +%!error ... +%! tLocationScaleDistribution(0, 1, "a") +%!error ... +%! tLocationScaleDistribution(0, 1, [1, 2]) +%!error ... +%! tLocationScaleDistribution(0, 1, NaN) + +## 'cdf' method +%!error ... +%! cdf (tLocationScaleDistribution, 2, "uper") +%!error ... +%! cdf (tLocationScaleDistribution, 2, 3) + +## 'paramci' method +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha", 0) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha", 1) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha", [0.5 2]) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha", "") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), "alpha", {0.05}) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "parameter", "mu", "alpha", {0.05}) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "parameter", {"lambda", "mu", "param"}) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "alpha", 0.01, "parameter", {"lambda", "mu", "param"}) +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "parameter", "param") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "alpha", 0.01, "parameter", "param") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "NAME", "value") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "alpha", 0.01, "NAME", "value") +%!error ... +%! paramci (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), ... +%! "alpha", 0.01, "parameter", "mu", "NAME", "value") + +## 'plot' method +%!error ... +%! plot (tLocationScaleDistribution, "Parent") +%!error ... +%! plot (tLocationScaleDistribution, "PlotType", 12) +%!error ... +%! plot (tLocationScaleDistribution, "PlotType", {"pdf", "cdf"}) +%!error ... +%! plot (tLocationScaleDistribution, "PlotType", "pdfcdf") +%!error ... +%! plot (tLocationScaleDistribution, "Discrete", "pdfcdf") +%!error ... +%! plot (tLocationScaleDistribution, "Discrete", [1, 0]) +%!error ... +%! plot (tLocationScaleDistribution, "Discrete", {true}) +%!error ... +%! plot (tLocationScaleDistribution, "Parent", 12) +%!error ... +%! plot (tLocationScaleDistribution, "Parent", "hax") + +## 'proflik' method +%!error ... +%! proflik (tLocationScaleDistribution, 2) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 4) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), [1, 2]) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), {1}) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ones (2)) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display") +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display", 1) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display", {1}) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display", {"on"}) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display", ["on"; "on"]) +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "Display", "onnn") +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! "NAME", "on") +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! {"NAME"}, "on") +%!error ... +%! proflik (tLocationScaleDistribution.fit (tlsrnd (0, 1, 1, [1, 100])), 1, ... +%! {[1 2 3 4]}, "Display", "on") + +## 'truncate' method +%!error ... +%! truncate (tLocationScaleDistribution) +%!error ... +%! truncate (tLocationScaleDistribution, 2) +%!error ... +%! truncate (tLocationScaleDistribution, 4, 2) + +## Catch errors when using array of probability objects with available methods +%!shared pd +%! pd = tLocationScaleDistribution(1, 1); +%! pd(2) = tLocationScaleDistribution(1, 3); +%!error cdf (pd, 1) +%!error icdf (pd, 0.5) +%!error iqr (pd) +%!error mean (pd) +%!error median (pd) +%!error negloglik (pd) +%!error paramci (pd) +%!error pdf (pd, 1) +%!error plot (pd) +%!error proflik (pd, 2) +%!error random (pd) +%!error std (pd) +%!error ... +%! truncate (pd, 2, 4) +%!error var (pd)