def _divide(self, hyperrectangle, safe, growing, iters): minanchor = self._getminanchor(growing, hyperrectangle) maxanchor = self._getmaxanchor(growing, hyperrectangle) lower = minanchor upper = maxanchor lower_res = lower upper_res = upper for i in range(1, iters): logger.debug("Anchors are %s and %s", lower, upper) center = (lower + upper) * pc.Rational(0.5) logger.debug("Evaluating at %s", center) results = self._checker.perform_sampling( [ParameterInstantiation.from_point(center, self._parameters)], True) logger.debug("Result: %s", float(list(results.values())[0])) if list(results.values())[0] > self.threshold: upper = center upper_res = center else: lower = center lower_res = center below_threshold_region = HyperRectangle.from_extremal_points( minanchor, lower_res, boundtype=BoundType.open) above_threshold_region = HyperRectangle.from_extremal_points( maxanchor, upper_res, boundtype=BoundType.open) upper_on_min_projected = Point(minanchor[0], upper_res[1]) max_on_min_projected = Point(minanchor[0], maxanchor[1]) min_on_max_projected = Point(maxanchor[0], minanchor[1]) lower_on_upper_projected = Point(lower_res[0], upper_res[1]) mini_region = HyperRectangle.from_extremal_points( upper_on_min_projected, lower_res, boundtype=BoundType.closed) midi_region = HyperRectangle.from_extremal_points( max_on_min_projected, upper_res, boundtype=BoundType.closed) maxi_region = HyperRectangle.from_extremal_points( min_on_max_projected, lower_on_upper_projected, boundtype=BoundType.closed) safe_regions = [above_threshold_region] bad_regions = [below_threshold_region] logger.debug( "Area above: %s; Area below %s; Unknown area: %s (%s %)", float(above_threshold_region.size()), float(below_threshold_region.size()), float(mini_region.size() + midi_region.size() + maxi_region.size()), float(100 * (mini_region.size() + midi_region.size() + maxi_region.size()) / hyperrectangle.size())) logger.debug("Result: lower: %s upper: %s", lower_res, upper_res) return RegionCheckResult.Splitted, (safe_regions, bad_regions, [ mini_region, midi_region, maxi_region ])
def test_hyperrectangle_vertices(): i = Interval(0.0, BoundType.closed, 2.0, BoundType.closed) j = Interval(4.0, BoundType.closed, 8.0, BoundType.closed) r = HyperRectangle(i, j) vertices = r.vertices() assert len(vertices) == 4 assert Point(0.0, 4.0) in vertices assert Point(2.0, 4.0) in vertices
def post(self): logger.debug("Samples post request") #print(self.request.body) sampling_information = json_decode(self.request.body) #print(sampling_information) coordinates = sampling_information # Get the current prism file and save it temporarily prism_files = self._get_session("prism-files", {}) #assert sampling_information["prism_file"] in prism_files #prism_file = PrismFile(prism_files[sampling_information["prism_file"]]) if coordinates is None: return self._json_error("Unable to read coordinates", 400) result = self._getResultData(self._get_session('current_result', None)) samples = self._get_session( 'samples', InstantiationResultDict(parameters=result.parameters)) socket = self._get_socket() sampling_interface = getSampler(self._get_session('sampler'), result) coordinates = [Point(Rational(x), Rational(y)) for x, y in coordinates] sample_points = result.parameters.instantiate(coordinates) new_samples = sampling_interface.perform_sampling(sample_points) if socket is not None: socket.send_samples(new_samples) samples.update(new_samples) self._set_session('samples', samples) return self._json_ok(_jsonSamples(new_samples))
def get_point(self, parameters): """Return the Point corresponding to this sample, given variable ordering provided as argument :param parameters: Must correspond to parameters of this sample point. :type parameters: Iterable[Parameter] """ return Point(*[self[par] for par in parameters])
def get_vertex(self, pick_min_bound): """ Get the vertex that corresponds to the left/right bound for the ith parameter, depending on the argument :param pick_min_bound: Array indicating to take the min (True) or the max (False) :return: """ assert len(pick_min_bound) == self.dimension() return Point( *[(interval.left_bound() if pmb else interval.right_bound()) for interval, pmb in zip(self.intervals, pick_min_bound)])
def vertices(self): result = [] for i in range(0, pow(2, self.dimension()), 1): num_bits = self.dimension() bits = [(i >> bit) & 1 for bit in range(num_bits - 1, -1, -1)] result.append( Point(*[(self.intervals[i].left_bound() if x == 0 else self.intervals[i].right_bound()) for i, x in zip(range(0, self.dimension()), bits)])) return result
def split_by_growing_rectangles(region): """ Split the region according to growing rectangles. :param region: Region. :return: New regions after splitting. """ logger.debug("Split region {}".format(region.region)) # Get all anchor points bounds = [(interv.left_bound(), interv.right_bound()) for interv in region.region.intervals] anchor_points = [Point(*val) for val in itertools.product(*bounds)] best_candidate = None for anchor in anchor_points: for anchor2, safe_anchor in region.samples: rectangle = HyperRectangle.from_extremal_points(anchor, anchor2, BoundType.closed) # TODO handle open intervals size = rectangle.size() if size <= 0 or size >= region.region.size(): # Rectangle too small or too large continue if best_candidate is not None and size <= best_candidate[0]: # Larger candidate already known continue # Check candidate valid = True for pt, safe in region.samples: if rectangle.contains(pt) and safe != safe_anchor: valid = False break if valid: # Found better candidate best_candidate = (size, anchor, anchor2) if best_candidate is None: # No good sample inside the region found -> split uniformly logger.debug("No candidate region found, split uniformly as fallback.") return HyperRectangleRegions.split_uniformly_in_every_dimension(region) logger.debug( "Candidate: {} for anchor {} and sample {}".format(best_candidate[0], best_candidate[1], best_candidate[2])) # Construct hyperrectangle for each anchor and the sample point result = [] for anchor in anchor_points: new_rectangle = HyperRectangle.from_extremal_points(anchor, best_candidate[2], BoundType.closed) result.append(new_rectangle) logger.debug("New regions:\n\t{}".format("\n\t".join([str(region) for region in result]))) return result
def test_perform_sampling(MCType, name): tool = MCType() prism_file = PrismFile(get_example_path("funny_defined", "fun.pm")) pctl_file = PctlFile(get_example_path("funny_defined", "property1.pctl")) tool.load_model_from_prismfile(prism_file) tool.set_pctl_formula(pctl_file.get(0)) parameters = copy.copy(prism_file.parameters) parameters.make_intervals_closed( pc.Rational(pc.Integer(1), pc.Integer(1000))) points = [(Point(pc.Rational(0.25), pc.Rational(0.5)))] sample_points = parameters.instantiate(points) assert len(sample_points) == 1 result = tool.perform_sampling(sample_points) assert len(result) == 1 for instantiation, val in result.items(): assert val == pc.Rational(0.75)
def perform_uniform_sampling(self, parameters, region, samples_per_dimension): """ Samples a uniform grid of points. Given a list of intervals (i.e., the first and last point; for each dimension, in order) and the number of samples per dimension, a uniformly-spaced grid of points (the cartesian product) is sampled. :param parameters: Parameters together with their region. :param samples_per_dimension: In how many points should the region be divided. """ logger.debug("Uniform sampling: Fallback to sampling list of samples") if samples_per_dimension <= 1: raise RuntimeError("No. of samples per dimension must be >= 2") # points evenly spaced over the interval, for each dimension ranges = [] if region: intervals = region.intervals else: intervals = parameters.get_parameter_bounds() for i in intervals: minNum = i.left_bound() if i.left_bound_type( ) == BoundType.closed else i.left_bound( ) + prophesy.config.configuration.get_sampling_epsilon() maxNum = i.right_bound() if i.right_bound_type( ) == BoundType.closed else i.right_bound( ) - prophesy.config.configuration.get_sampling_epsilon() ranges.append( map( Rational, linspace(float(minNum), float(maxNum), samples_per_dimension))) # turned into grid via cartesian product all_points = itertools.product(*ranges) all_points = [Point(*coords) for coords in all_points] sample_points = parameters.instantiate(all_points) return self.perform_sampling(sample_points)
def _shrink(self, hyperrectangle, safe, growing): def _iterate_above(min, max): i = 0 result = None center = (min + max) * pc.Rational(0.5) while i < 7: value = self._evaluate(center) if value > self.threshold: result = center center = (center + min) * pc.Rational(0.5) i += 1 else: center = (center + max) * pc.Rational(0.5) i += 2 return result def _iterate_below(min, max): i = 0 result = None center = (min + max) * pc.Rational(0.5) while i < 7: value = self._evaluate(center) if value < self.threshold: result = center center = (center + max) * pc.Rational(0.5) i += 1 else: center = (center + min) * pc.Rational(0.5) i += 2 return result minanchor = self._getminanchor(growing, hyperrectangle) maxanchor = self._getmaxanchor(growing, hyperrectangle) value = self._evaluate(minanchor) if value > self.threshold: logger.debug("Minimum value is {}".format(value)) return RegionCheckResult.Splitted, ([hyperrectangle], [], []) value = self._evaluate(maxanchor) if value < self.threshold: logger.debug("Maximum value is {}".format(value)) return RegionCheckResult.Splitted, ([], [hyperrectangle], []) safe_regions = [] # [above_threshold_region] bad_regions = [] # [below_threshold_region] # First, shrink horizontally: max_on_min_projected = Point(minanchor[0], maxanchor[1]) res = _iterate_above(minanchor, max_on_min_projected) if res is not None: logger.debug("Found {}".format(res)) reg = HyperRectangle.from_extremal_points( res, maxanchor, boundtype=BoundType.closed) assert reg.size() > pc.Rational(0) logger.debug("Add safe region: {} (type 1)".format(reg)) safe_regions.append(reg) maxanchor = Point(maxanchor[0], res[1]) min_on_max_projected = Point(maxanchor[0], minanchor[1]) res = _iterate_above(minanchor, min_on_max_projected) if res is not None: logger.debug("Found {}".format(res)) reg = HyperRectangle.from_extremal_points( res, maxanchor, boundtype=BoundType.closed) assert reg.size() > pc.Rational(0) logger.debug("Add safe region: {} (type 2)".format(reg)) safe_regions.append(reg) maxanchor = Point(res[0], maxanchor[1]) max_on_min_projected = Point(minanchor[0], maxanchor[1]) res = _iterate_below(max_on_min_projected, maxanchor) if res is not None: logger.debug("Found {}".format(res)) reg = HyperRectangle.from_extremal_points( res, minanchor, boundtype=BoundType.closed) logger.debug("Add bad region: {} (type 1)".format(reg)) assert reg.size() > pc.Rational(0) bad_regions.append(reg) minanchor = Point(res[0], minanchor[1]) min_on_max_projected = Point(maxanchor[0], minanchor[1]) res = _iterate_below(min_on_max_projected, maxanchor) if res is not None: logger.debug("Found {}".format(res)) reg = HyperRectangle.from_extremal_points( res, minanchor, boundtype=BoundType.closed) logger.debug("Add bad region: {} (type 2)".format(reg)) assert reg.size() > pc.Rational(0) bad_regions.append(reg) minanchor = Point(minanchor[0], res[1]) remaining = HyperRectangle.from_extremal_points( minanchor, maxanchor, boundtype=BoundType.closed) assert remaining.size() > 0 logger.debug("Remaining: {}".format(remaining)) logger.debug( "Safe area {}, Bad area {}, Remaining area {} ({} %)".format( float(sum([r.size() for r in safe_regions])), float(sum([r.size() for r in bad_regions])), float(remaining.size()), 100 * float(remaining.size() / hyperrectangle.size()))) return RegionCheckResult.Splitted, (safe_regions, bad_regions, [remaining])
def test_to_float(): p = Point(*[3, 4]).to_float() assert p[0] == 3.0
def center(self): return Point(*[interval.center() for interval in self.intervals])
def read_samples_file(path, parameters): """ Reads sample files. The first line specifies the parameters (with an optional "Result" for the last column). The second line optionally specifies a threshold. This is important if we have binary samples, (for which we do not know the value, but just whether it is above or below the threshold). The remaining lines give the parameter values and the value. This value is either a number or "above" or "below". :param path: :return: """ threshold = None with open(path, 'r') as f: lines = [l.strip() for l in f.readlines()] if len(lines) <= 2: raise RuntimeError("Samples file is empty or malformed") # read first line with variable names parameter_names = lines[0].split() if parameter_names[-1] == "Result": parameter_names = parameter_names[:-1] start = 1 for par_name, par in zip(parameter_names, parameters): if par_name != par.name: raise ValueError( "Parameter names {} do not coincide with given parameters {}" .format(parameter_names, parameters)) #Ignore thresholds if lines[1].startswith("Threshold"): if len(lines[1].split()) != 2: raise IOError("Invalid input on line 2") threshold = Rational(lines[1].split()[1]) start += 1 samples = InstantiationResultDict(parameters=parameters) skip_next = False for i, line in enumerate(lines[start:]): if skip_next: skip_next = False continue items = line.split() if len(items) - 1 != len(parameter_names): # Prism reports that probs are negative: if line.find("are negative") > 0: coords = map(Rational, items[:len(parameter_names)]) samples[ParameterInstantiation.from_point( Point(*coords), parameters)] = InstantiationResultFlag.NOT_WELLDEFINED skip_next = True continue logger.error("Invalid input in %s on line %s: '%s'", path, str(i + start), line) continue if items[-1] == "below": #TODO raise NotImplementedError( "Inexact sampling results are not yet supported in v2") #value = SAMPLE_BELOW elif items[-1] == "above": #TODO raise NotImplementedError( "Inexact sampling results are not yet supported in v2") elif items[-1] == "InstantiationResultFlag.NOT_WELLDEFINED": value = InstantiationResultFlag.NOT_WELLDEFINED else: value = Rational(items[-1]) coords = map(Rational, items[:-1]) samples[ParameterInstantiation.from_point(Point(*coords), parameters)] = value logger.debug("Parameters: %s", str(parameters)) return parameters, threshold, samples
def _coords_to_rational_point(coords): return Point(*(pc.Rational(component) for component in coords))