def instantiate(self, one_or_more_pointlikes): """Create ParameterInstantiation(s) from point-like objects. Returns the same shape as the input, i.e., if the input is a single point-like object (i.e., iterable, hopefully of numbers), returns a single ParameterInstantiation; for a list of points, returns a list of ParameterInstantiations. """ def looks_like_list_of_points(a): return isinstance(a, collections.abc.Sequence) and isinstance( a[0], collections.abc.Sized) and len(a[0]) == len(self) def looks_like_a_single_point(a): return isinstance(a, collections.abc.Sized) and len(a) == len(self) if looks_like_list_of_points(one_or_more_pointlikes): return [ ParameterInstantiation.from_point(p, self) for p in one_or_more_pointlikes ] elif looks_like_a_single_point(one_or_more_pointlikes): return ParameterInstantiation.from_point(one_or_more_pointlikes, self) else: raise RuntimeError( "Unexpected shape. This *may* be legitimate. See code and possibly add this case." )
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 fail_region(self, homogeneity=False): logger.debug("Failed checking the region.") # Split region and try again regionelem = self.regions[0] # failure ony applies for region that was already consistent self.regions = self.regions[1:] if regionelem.empty_checks == 1 and not homogeneity: logger.debug("Region has not been checked for inverse.") dist = self._compute_closest_inverse_sample(not regionelem.safe, regionelem.region) self.regions.insert(0, _AnnotatedRegion(regionelem.region, regionelem.samples, not regionelem.safe, well_defined=regionelem.well_defined, graph_preserving=regionelem.graph_preserving, closest_inverse_sample_distance=dist)) self.regions[0].empty_checks = 2 else: logger.debug("Region has to be split.") newelems = self.split(regionelem) for newregion in newelems: wd = regionelem.well_defined gp = regionelem.graph_preserving if self.checker.supports_only_closed_regions(): newregion = newregion.close() elif True:#self.checker.prefers_closed_regions(): #newregion = newregion.close() solver = Z3CliSolver() solver.run() wd_res = check_welldefinedness(solver, self.parameters, newregion.close(), self.gp_constraints + self.wd_constraints) solver.stop() if wd_res == WelldefinednessResult.Welldefined: newregion = newregion.close() logger.debug("Add region to consider later: {}".format(newregion)) newsamples = [] for pt, safe in regionelem.samples: if not newregion.contains(pt): continue newsamples.append((pt, safe)) if len(newsamples) == 0: if self.sample_generator: logger.debug("Sampling as there is no sample in the region.") sampledict = self.sample_generator.perform_sampling( [ParameterInstantiation.from_point(newregion.center(), self.parameters)], surely_welldefined=True) hypothesis = list(sampledict.items())[0][1] < self.threshold newsamples = [(newregion.center(), hypothesis)] else: hypothesis = self._guess_hypothesis(newregion) else: hypothesis = regionelem.safe dist = self._compute_closest_inverse_sample(hypothesis, newregion) self.regions.insert(0, _AnnotatedRegion(newregion, newsamples, hypothesis, well_defined=wd, graph_preserving=gp, closest_inverse_sample_distance=dist)) self._sort_regions()
def _evaluate(self, smt_model): sample = ParameterInstantiation() for par in self.parameters: value = smt_model[par.name] rational = pc.Rational(value) sample[par] = rational value = self.model_explorer.perform_sampling([sample])[sample] return InstantiationResult(sample, value)
def _smt_model_to_sample(self, smt_model): sample = ParameterInstantiation() try: for par in self.parameters: value = smt_model[par.name] rational = pc.Rational(value) sample[par] = rational except ValueError: logger.debug("Cannot translate into a rational instance") return None return sample
def _evaluate(self, point): logger.debug("Evaluating at {}".format(point)) results = self._checker.perform_sampling( [ParameterInstantiation.from_point(point, self._parameters)], True) return list(results.values())[0]
def check_region(self, region, depth=0): """ Check if the given region can be assumed safe or bad based on known samples. If samples are mixed, split the region and retry. Resulting regions are added to self.regions. :param region: Region. :param depth: Maximal depth for region refining. """ if depth >= self.check_depth: self.parked_regions.append(region) return if self.checker.supports_only_closed_regions(): region.region = region.region.close() # TODO check graph preserving seperately. if region.well_defined == WelldefinednessResult.Undecided: logger.info("Check well-definedness for the region") solver = Z3CliSolver() solver.run() wd_res = check_welldefinedness(solver, self.parameters, region.region, self.gp_constraints + self.wd_constraints) solver.stop() if wd_res == WelldefinednessResult.Illdefined: region.well_defined = WelldefinednessResult.Illdefined self.regions.append(region) return if wd_res == WelldefinednessResult.Welldefined: region.well_defined = WelldefinednessResult.Welldefined region.graph_preserving = WelldefinednessResult.Welldefined if region.well_defined == WelldefinednessResult.Welldefined: mixed = True if self.sample_generator and len(region.samples) == 0: logger.debug("Sampling as there is no sample in the region.") sampledict = self.sample_generator.perform_sampling([ParameterInstantiation.from_point(region.region.center(), self.parameters)], surely_welldefined=True) region.samples = [(region.region.center(), list(sampledict.items())[0][1] > self.threshold)] region.empty_checks = 0 if len(region.samples) == 1: hypothesis_safe = region.samples[0][1] mixed = False elif len(region.samples) == 0: hypothesis_safe = self._guess_hypothesis(region.region) mixed = False elif all([sample[1] for sample in region.samples]): # All safe hypothesis_safe = True mixed = False elif all([not sample[1] for sample in region.samples]): # All bad hypothesis_safe = False mixed = False if not mixed: dist = self._compute_closest_inverse_sample(hypothesis_safe, region.region) region.safe = hypothesis_safe region.closest_inverse_sample_distance = dist self.regions.append(region) return # Mixed region, split. newelems = self.split(region) if newelems is None: return None for newregion in newelems: newsamples = [] for pt, safe in region.samples: if newregion.contains(pt): newsamples.append((pt, safe)) self.check_region(_AnnotatedRegion(newregion, newsamples, well_defined=region.well_defined, graph_preserving=region.graph_preserving), depth + 1)
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 _mc_check(self, param_values): parameter_assignments = dict([[x, param_values[x.id]] for x in self._variables]) sample = ParameterInstantiation() for x, val in parameter_assignments.items(): sample[x] = pc.Rational(val) return self._model_explorer.mc_single_point(sample)
def _evaluate_result(self, param_values): parameter_assignments = dict([[x, param_values[x.id]] for x in self._variables]) sample = ParameterInstantiation() for x, val in parameter_assignments.items(): sample[x] = pc.Rational(val) return sample, self._model_explorer.perform_sampling([sample])