diff --git a/prism/src/param/ParamRewardStruct.java b/prism/src/param/ParamRewardStruct.java index a32d9e45..0584b485 100644 --- a/prism/src/param/ParamRewardStruct.java +++ b/prism/src/param/ParamRewardStruct.java @@ -26,6 +26,9 @@ package param; +import prism.PrismException; +import prism.PrismNotSupportedException; + /** * Reward structure for parametric model. * We only consider rewards assigned to a certain nondeterministic choice, @@ -122,7 +125,47 @@ final class ParamRewardStruct { { return rewards[choice]; } - + + /** + * Returns true if there are negative rewards. + *
+ * Can only be called on a reward structure that + * has been instantiated, i.e., where all rewards + * are constant rational numbers. + *
+ * If either +/-Inf or NaN are encountered, an exception is thrown. + */ + public boolean hasNegativeRewards() throws PrismException + { + for (Function r : rewards) { + if (!r.isConstant()) { + throw new UnsupportedOperationException("Can not check for negative rewards against non-constant functions"); + } + BigRational v = r.asBigRational(); + if (v.isSpecial()) { + throw new PrismNotSupportedException("Reward value " + v + " not supported"); + } + if (r.asBigRational().signum() < 0) { + return true; + } + } + + return false; + } + + /** + * Check for non-normal reward values, i.e., +/-Inf and NaN, + * throw exception if detected. + */ + public void checkForNonNormalRewards() throws PrismException + { + for (Function r : rewards) { + if (r.isInf() || r.isMInf() || r.isNaN()) { + throw new PrismNotSupportedException("Reward value " + r + " not supported"); + } + } + } + @Override public String toString() { StringBuffer result = new StringBuffer(); diff --git a/prism/src/param/ValueComputer.java b/prism/src/param/ValueComputer.java index 2afd8fd4..a165bbfa 100644 --- a/prism/src/param/ValueComputer.java +++ b/prism/src/param/ValueComputer.java @@ -294,12 +294,16 @@ final class ValueComputer extends PrismComponent } } - private RegionValues computeUnboundedMC(Region region, StateValues b1, StateValues b2, ParamRewardStruct rew) + private RegionValues computeUnboundedMC(Region region, StateValues b1, StateValues b2, ParamRewardStruct rew) throws PrismException { // Convert to MutablePMC, using trivial scheduler (take first choice everywhere) Scheduler trivialScheduler = new Scheduler(model); MutablePMC pmc = buildAlterablePMCForReach(model, b1, b2, trivialScheduler, rew); + if (rew != null && mode == ParamMode.EXACT) { + rew.checkForNonNormalRewards(); + } + StateValues values = computeValues(pmc, model.getFirstInitialState()); return regionFactory.completeCover(values); } @@ -395,6 +399,9 @@ final class ValueComputer extends PrismComponent ParamRewardStruct rewConcrete = null; if (rew != null) { rewConcrete = rew.instantiate(point); + if (mode == ParamMode.EXACT && rewConcrete.hasNegativeRewards()) { + throw new PrismNotSupportedException(mode.Engine() + " currently does not support negative rewards in reachability reward computations"); + } } Scheduler scheduler = lookupScheduler(point, concrete, PropType.REACH, b1, b2, min, rewConcrete);