From 57e23a489121d33a084780e6ca8999e588d401af Mon Sep 17 00:00:00 2001 From: Andreas Zeller Date: Sun, 5 Jan 2025 19:29:21 +0100 Subject: [PATCH] "Friendly" output of requirements and features --- notebooks/Alhazen.ipynb | 87 ++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/notebooks/Alhazen.ipynb b/notebooks/Alhazen.ipynb index bd07ade3..532dad40 100644 --- a/notebooks/Alhazen.ipynb +++ b/notebooks/Alhazen.ipynb @@ -605,10 +605,12 @@ " key : The feature key (e.g., the chosen alternative or rule itself).\n", " '''\n", "\n", - " def __init__(self, name: str, rule: str, key: str) -> None:\n", + " def __init__(self, name: str, rule: str, key: str, /,\n", + " friendly_name: str = None) -> None:\n", " self.name = name\n", " self.rule = rule\n", " self.key = key\n", + " self._friendly_name = friendly_name or name\n", " super().__init__()\n", "\n", " def __repr__(self) -> str:\n", @@ -619,6 +621,9 @@ " def name_rep(self) -> str:\n", " pass\n", "\n", + " def friendly_name(self) -> str:\n", + " return self._friendly_name\n", + "\n", " @abstractmethod\n", " def get_feature_value(self, derivation_tree) -> float:\n", " '''Returns the feature value for a given derivation tree of an input.'''\n", @@ -648,8 +653,9 @@ " key : The feature key, equal to the rule attribute for production features,\n", " or equal to the corresponding alternative for alternative features.\n", " '''\n", - " def __init__(self, name: str, rule: str, key: str) -> None:\n", - " super().__init__(name, rule, key)\n", + " def __init__(self, name: str, rule: str, key: str,\n", + " friendly_name: str = None) -> None:\n", + " super().__init__(name, rule, key, friendly_name=friendly_name)\n", "\n", " def name_rep(self) -> str:\n", " if self.rule == self.key:\n", @@ -709,8 +715,8 @@ " e.g., 'num()'\n", " rule : The production rule.\n", " '''\n", - " def __init__(self, name: str, rule: str) -> None:\n", - " super().__init__(name, rule, rule)\n", + " def __init__(self, name: str, rule: str, friendly_name: str = None) -> None:\n", + " super().__init__(name, rule, rule, friendly_name=friendly_name)\n", "\n", " def name_rep(self) -> str:\n", " return f\"num({self.key})\"\n", @@ -766,7 +772,11 @@ " features.append(ExistenceFeature(f\"exists({rule})\", rule, rule))\n", " # add all alternatives\n", " for count, expansion in enumerate(grammar[rule]):\n", - " features.append(ExistenceFeature(f\"exists({rule}@{count})\", rule, expansion))\n", + " name = f\"exists({rule}@{count})\"\n", + " friendly_name = f\"{rule} == {repr(expansion)}\"\n", + " feature = ExistenceFeature(name, rule, expansion,\n", + " friendly_name=friendly_name)\n", + " features.append(feature)\n", "\n", " return features" ] @@ -825,7 +835,11 @@ " for key in derivable_chars:\n", " # Check if derivable chars contain only numeric chars\n", " if len(derivable_chars[key] - numeric_chars) == 0:\n", - " features.append(NumericInterpretation(f\"num({key})\", key))\n", + " name = f\"num({key})\"\n", + " friendly_name = f\"{key}\"\n", + "\n", + " features.append(NumericInterpretation(f\"num({key})\", key,\n", + " friendly_name=friendly_name))\n", "\n", " return features" ] @@ -849,6 +863,15 @@ "get_all_features(CALC_GRAMMAR)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[f.friendly_name() for f in get_all_features(CALC_GRAMMAR)] " + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -2161,7 +2184,25 @@ " self.value = value\n", "\n", " def __str__(self):\n", - " return f\"Requirement({self.feature.name} {self.quant} {self.value})\"" + " return f\"Requirement({self.feature.name} {self.quant} {self.value})\"\n", + "\n", + " def __repr__(self):\n", + " return f\"Requirement({self.feature.name}, {self.quant}, {self.value})\"\n", + "\n", + " def friendly(self):\n", + " def value(x):\n", + " try:\n", + " return float(x)\n", + " except Exception:\n", + " return None\n", + "\n", + " if isinstance(self.feature, ExistenceFeature):\n", + " if value(self.value) > 0:\n", + " return f\"{self.feature.friendly_name()}\"\n", + " elif value(self.value) < 0:\n", + " return f\"not {self.feature.friendly_name()}\"\n", + "\n", + " return f\"{self.feature.friendly_name()} {self.quant} {self.value}\"" ] }, { @@ -2181,12 +2222,14 @@ " self.requirements: List[SpecRequirement] = requirements\n", "\n", " def __str__(self):\n", - " # Handle first element\n", - " s = f\"{str(self.requirements[0])}\"\n", - " for count in range(1, len(self.requirements)):\n", - " s += (\", \" + str(self.requirements[count]))\n", + " s = \", \".join(str(r) for r in self.requirements)\n", + " return f\"InputSpecification({s})\"\n", "\n", - " return f\"InputSpecification({s})\"" + " def friendly(self):\n", + " return \" and \".join(r.friendly() for r in self.requirements)\n", + "\n", + " def __repr__(self):\n", + " return self.__str__()" ] }, { @@ -2339,17 +2382,18 @@ "metadata": {}, "source": [ "We implement a _Grammar-Based Input Generator_ that generates new input samples from a List of `Input Specifications`.\n", - "The Input Specifications are extracted from the decision tree boundaries in the previous Activity 3: _RequirementExtraction_\n", + "The Input Specifications are extracted from the decision tree boundaries in the previous Activity 3: _RequirementExtraction_.\n", + "\n", "An Input Specification consists of **1 to n** many predicates or requirements (e.g. feature '>=' value, or 'num(term) <= 13').\n", - "We generate a new input for each InputSpecification.\n", - "The new input fulfills all the given requirements of an IputSpecification." + "We generate a new input for each `InputSpecification`.\n", + "The new input fulfills all the given requirements of an InputSpecification." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Info:** For furter details, please refer to Section 4.4 and 4.5 of the paper and the Chapter Efficient Grammar Fuzzing in the fuzzingbook." + "**Info:** For further details, please refer to Section 4.4 and 4.5 of the paper and the Chapter Efficient Grammar Fuzzing in the fuzzingbook." ] }, { @@ -2679,15 +2723,14 @@ " self._data.drop(['oracle'], axis=1))\n", " print(f\" New input specifications:\")\n", " for spec in new_input_specifications:\n", - " print(f\" {str(spec)}\")\n", + " print(f\" {spec.friendly()}\")\n", "\n", " # generate new inputs according to the new input specifications\n", " new_samples = generate_samples(self._grammar,\n", " new_input_specifications,\n", " self._generator_timeout)\n", " print(f\" New samples:\")\n", - " for sample in new_samples:\n", - " print(f\" {str(sample)}\")\n", + " print(f\" {\", \".join(new_samples)}\")\n", " self._previous_samples = new_samples" ] }, @@ -2749,8 +2792,8 @@ "metadata": {}, "outputs": [], "source": [ - "all_features = extract_existence(CALC_GRAMMAR) + extract_numeric(CALC_GRAMMAR)\n", - "all_feature_names = [f.name for f in all_features]" + "all_features = get_all_features(CALC_GRAMMAR)\n", + "all_feature_names = [f.friendly_name() for f in all_features]" ] }, {