diff --git a/pyRDDLGym/core/intervals.py b/pyRDDLGym/core/intervals.py index c9651110..d1cf9e92 100644 --- a/pyRDDLGym/core/intervals.py +++ b/pyRDDLGym/core/intervals.py @@ -26,6 +26,7 @@ class IntervalAnalysisStrategy(Enum): PERCENTILE = 2 MEAN = 3 + class RDDLIntervalAnalysis: def __init__( @@ -43,8 +44,7 @@ def __init__( :param percentiles: percentiles used to compute bounds when strategy is set to PERCENTILE ''' self.rddl = rddl - self.logger = logger - + self.logger = logger self.strategy = strategy self.percentiles = percentiles @@ -914,9 +914,8 @@ def _bound_random(self, expr, intervals): return self._bound_student(expr, intervals) elif name == 'Gumbel': return self._bound_gumbel(expr, intervals) - # not implemented - # elif name == 'Laplace': - # return self._bound_laplace(expr, intervals) + elif name == 'Laplace': + return self._bound_laplace(expr, intervals) elif name == 'Cauchy': return self._bound_cauchy(expr, intervals) elif name == 'Gompertz': @@ -941,12 +940,7 @@ def _bound_random_kron(self, expr, intervals): args = expr.args arg, = args - if self.strategy == IntervalAnalysisStrategy.PERCENTILE: - raise NotImplementedError("Percentile strategy is not implemented for Kronecker delta distribution yet.") - - if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Kronecker delta distribution yet.") - + # SUPPORT, PERCENTILE or MEAN strategy return self._bound(arg, intervals) def _bound_random_dirac(self, expr, intervals): @@ -966,7 +960,9 @@ def _bound_uniform(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Uniform distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Uniform distribution yet.") + lower = (la + lb) / 2 + upper = (ua + ub) / 2 + return (lower, upper) lower = la upper = ub @@ -979,13 +975,10 @@ def _bound_bernoulli(self, expr, intervals): if self.strategy == IntervalAnalysisStrategy.PERCENTILE: lower_percentile, upper_percentile = self.percentiles - lower = np.zeros(shape=np.shape(lp), dtype=np.int64) upper = np.ones(shape=np.shape(up), dtype=np.int64) - lower = self._mask_assign(lower, lower_percentile > (1 - lp), 1) upper = self._mask_assign(upper, upper_percentile <= (1 - up), 0) - return (lower, upper) if self.strategy == IntervalAnalysisStrategy.MEAN: @@ -1007,13 +1000,11 @@ def _bound_normal(self, expr, intervals): if self.strategy == IntervalAnalysisStrategy.PERCENTILE: # mean + std * normal_inverted_cdf(p) lower_percentile, upper_percentile = self.percentiles - lower = lm + np.sqrt(lv) * stats.norm.ppf(lower_percentile) upper = um + np.sqrt(uv) * stats.norm.ppf(upper_percentile) return (lower, upper) if self.strategy == IntervalAnalysisStrategy.MEAN: - # mean return (lm, um) # SUPPORT strategy @@ -1032,7 +1023,7 @@ def _bound_poisson(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Poisson distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Poisson distribution yet.") + return (lp, up) lower = np.zeros(shape=np.shape(lp), dtype=np.int64) upper = np.full(shape=np.shape(up), fill_value=np.inf, dtype=np.float64) @@ -1047,7 +1038,7 @@ def _bound_exponential(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Exponential distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Exponential distribution yet.") + return (ls, us) lower = np.zeros(shape=np.shape(ls), dtype=np.float64) upper = np.full(shape=np.shape(us), fill_value=np.inf, dtype=np.float64) @@ -1062,7 +1053,6 @@ def _bound_weibull(self, expr, intervals): if self.strategy == IntervalAnalysisStrategy.PERCENTILE: # scale * (-ln(1 - p))^(1 / shape) lower_percentile, upper_percentile = self.percentiles - lower = lsc * (-np.log(1 - lower_percentile) ) ** (1 / lsh) upper = usc * (-np.log(1 - upper_percentile) ) ** (1 / ush) return (lower, upper) @@ -1088,7 +1078,8 @@ def _bound_gamma(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Gamma distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Gamma distribution yet.") + # shape * scale + return RDDLIntervalAnalysis._bound_arithmetic_expr((lsh, ush), (lsc, usc), '*') lower = np.zeros(shape=np.shape(lsh), dtype=np.float64) upper = np.full(shape=np.shape(usc), fill_value=np.inf, dtype=np.float64) @@ -1104,7 +1095,8 @@ def _bound_binomial(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Binomial distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Binomial distribution yet.") + # n * p + return RDDLIntervalAnalysis._bound_arithmetic_expr((ln, un), (lp, up), '*') lower = np.zeros(shape=np.shape(ln), dtype=np.int64) upper = np.copy(un) @@ -1130,15 +1122,16 @@ def _bound_beta(self, expr, intervals): def _bound_geometric(self, expr, intervals): args = expr.args - p, = args - + p, = args (lp, up) = self._bound(p, intervals) if self.strategy == IntervalAnalysisStrategy.PERCENTILE: raise NotImplementedError("Percentile strategy is not implemented for Geometric distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Geometric distribution yet.") + # 1 / p + one = np.ones_like(up) + return RDDLIntervalAnalysis._bound_arithmetic_expr((one, one), (lp, up), '/') lower = np.ones(shape=np.shape(lp), dtype=np.int64) upper = np.full(shape=np.shape(up), fill_value=np.inf, dtype=np.float64) @@ -1169,7 +1162,9 @@ def _bound_student(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Student distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Student distribution yet.") + lower = np.zeros_like(ld) + upper = np.zeros_like(ud) + return (lower, upper) lower = np.full(shape=np.shape(ld), fill_value=-np.inf, dtype=np.float64) upper = np.full(shape=np.shape(ud), fill_value=+np.inf, dtype=np.float64) @@ -1185,7 +1180,25 @@ def _bound_gumbel(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Gumbel distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Gumbel distribution yet.") + lower = lm + 0.577215664901532 * ls + upper = um + 0.577215664901532 * us + return (lower, upper) + + lower = np.full(shape=np.shape(lm), fill_value=-np.inf, dtype=np.float64) + upper = np.full(shape=np.shape(um), fill_value=+np.inf, dtype=np.float64) + return (lower, upper) + + def _bound_laplace(self, expr, intervals): + args = expr.args + mean, scale = args + (lm, um) = self._bound(mean, intervals) + (ls, us) = self._bound(scale, intervals) + + if self.strategy == IntervalAnalysisStrategy.PERCENTILE: + raise NotImplementedError("Percentile strategy is not implemented for Laplace distribution yet.") + + if self.strategy == IntervalAnalysisStrategy.MEAN: + return (lm, um) lower = np.full(shape=np.shape(lm), fill_value=-np.inf, dtype=np.float64) upper = np.full(shape=np.shape(um), fill_value=+np.inf, dtype=np.float64) @@ -1201,7 +1214,7 @@ def _bound_cauchy(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Cauchy distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Cauchy distribution yet.") + raise ValueError("The mean of a Cauchy distribution is not defined.") lower = np.full(shape=np.shape(lm), fill_value=-np.inf, dtype=np.float64) upper = np.full(shape=np.shape(um), fill_value=+np.inf, dtype=np.float64) @@ -1232,7 +1245,7 @@ def _bound_chisquare(self, expr, intervals): raise NotImplementedError("Percentile strategy is not implemented for Chi-square distribution yet.") if self.strategy == IntervalAnalysisStrategy.MEAN: - raise NotImplementedError("Mean strategy is not implemented for Chi-square distribution yet.") + return (ld, ud) lower = np.zeros(shape=np.shape(ld), dtype=np.float64) upper = np.full(shape=np.shape(ud), fill_value=np.inf, dtype=np.float64) @@ -1278,4 +1291,5 @@ def _bound_discrete_pvar(self, expr, intervals, unnorm): lower_prob, upper_prob = self._bound(arg, intervals) bounds = [(lower_prob[..., i], upper_prob[..., i]) for i in range(lower_prob.shape[-1])] - return self._bound_discrete_helper(bounds) \ No newline at end of file + return self._bound_discrete_helper(bounds) + \ No newline at end of file