Example #1
0
    def format_value(self, value, isToAlign=False, format_target="html"):
        """
        Format a value nicely for human-readable output (including rounding).

        @param value: the value to format
        @param isToAlign: if True, spaces will be added to the returned String representation to align it to all
            other values in this column, correctly
        @param format_target the target the value should be formatted for
        @return: a formatted String representation of the given value.
        """
        # Only format counts and measures
        if self.type.type != ColumnType.count and self.type.type != ColumnType.measure:
            return value

        if format_target not in POSSIBLE_FORMAT_TARGETS:
            raise ValueError('Unknown format target')

        if value is None or value == '':
            return ''

        # If the number ends with "s" or another unit, remove it.
        # Units should not occur in table cells, but in the table head.
        number_str = util.remove_unit(str(value).strip())
        number = float(number_str)

        if isnan(number):
            return 'NaN'
        elif number == inf:
            return 'Inf'
        elif number == -inf:
            return '-Inf'

        # Apply the scale factor to the value
        if self.scale_factor is not None:
            number *= self.scale_factor

        number_of_significant_digits = self.number_of_significant_digits
        max_dec_digits = 0
        if number_of_significant_digits is None and format_target is "tooltip_stochastic":
            return str(round(number, DEFAULT_TOOLTIP_PRECISION))

        elif self.type.type == ColumnType.measure:
            if number_of_significant_digits is None and format_target is not "csv":
                number_of_significant_digits = DEFAULT_TIME_PRECISION
            max_dec_digits = self.type.max_decimal_digits

        if number_of_significant_digits is not None:
            current_significant_digits = _get_significant_digits(number_str)
            return _format_number(number, current_significant_digits,
                                  number_of_significant_digits, max_dec_digits,
                                  isToAlign, format_target)
        else:
            if number == float(number_str) or isnan(number) or isinf(number):
                # TODO remove as soon as scaled values are handled correctly
                return number_str
            if int(number) == number:
                number = int(number)
            return str(number)
Example #2
0
    def format_value(self, value, isToAlign=False, format_target="html"):
        """
        Format a value nicely for human-readable output (including rounding).

        @param value: the value to format
        @param isToAlign: if True, spaces will be added to the returned String representation to align it to all
            other values in this column, correctly
        @param format_target the target the value should be formatted for
        @return: a formatted String representation of the given value.
        """
        if format_target not in POSSIBLE_FORMAT_TARGETS:
            raise ValueError('Unknown format target')

        if value is None:
            return ''

        # If the number ends with "s" or another unit, remove it.
        # Units should not occur in table cells, but in the table head.
        number_str = util.remove_unit(str(value).strip())

        try:
            number = float(number_str)
        except ValueError:  # If value is no float, don't format it.
            return value

        # Apply the scale factor to the value
        if self.scale_factor is not None:
            number *= self.scale_factor

        number_of_significant_digits = self.number_of_significant_digits
        max_dec_digits = 0
        if number_of_significant_digits is None and format_target is "tooltip_stochastic":
            return str(round(number, DEFAULT_TOOLTIP_PRECISION))

        elif self.type.type == ColumnType.measure:
            if number_of_significant_digits is None and format_target is not "csv":
                number_of_significant_digits = DEFAULT_TIME_PRECISION
            max_dec_digits = self.type.max_decimal_digits

        if number_of_significant_digits is not None:
            current_significant_digits = _get_significant_digits(number_str)
            return _format_number(number, current_significant_digits, number_of_significant_digits, max_dec_digits, isToAlign, format_target)
        else:
            if number == float(number_str):
                # TODO remove as soon as scaled values are handled correctly
                return number_str
            if int(number) == number:
                number = int(number)
            return str(number)
Example #3
0
def main(args=None):
    if args is None:
        args = sys.argv

    parser = argparse.ArgumentParser(
        fromfile_prefix_chars="@",
        description=
        """Create CSV tables for quantile plots with the results of a benchmark execution.
           The CSV tables are similar to those produced with table-generator,
           but have an additional first column with the index for the quantile plot,
           and they are sorted.
           The output is written to stdout.
           Part of BenchExec: https://github.com/sosy-lab/benchexec/""",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument(
        "result",
        metavar="RESULT",
        type=str,
        nargs="+",
        help="XML files with result produced by benchexec",
    )
    parser.add_argument(
        "--correct-only",
        action="store_true",
        dest="correct_only",
        help=
        "only use correct results (recommended, implied if --score-based is used)",
    )
    parser.add_argument(
        "--score-based",
        action="store_true",
        dest="score_based",
        help="create data for score-based quantile plot",
    )
    parser.add_argument(
        "--sort-by",
        metavar="SORT",
        default="cputime",
        dest="column_identifier",
        type=str,
        help=
        "column identifier for sorting the values, e.g. 'cputime' or 'walltime'",
    )

    options = parser.parse_args(args[1:])

    # load results
    run_set_result = tablegenerator.RunSetResult.create_from_xml(
        options.result[0],
        tablegenerator.parse_results_file(options.result[0]))
    for results_file in options.result[1:]:
        run_set_result.append(results_file,
                              tablegenerator.parse_results_file(results_file))
    run_set_result.collect_data(options.correct_only or options.score_based)

    # select appropriate results
    if options.score_based:
        start_index = 0
        index_increment = lambda run_result: run_result.score  # noqa: E731
        results = []
        for run_result in run_set_result.results:
            if run_result.score is None:
                sys.exit("No score available for task {0}, "
                         "cannot produce score-based quantile data.".format(
                             run_result.task_id[0]))

            if run_result.category == result.CATEGORY_WRONG:
                start_index += run_result.score
            elif run_result.category == result.CATEGORY_MISSING:
                sys.exit("Property missing for task {0}, "
                         "cannot produce score-based quantile data.".format(
                             run_result.task_id[0]))
            elif run_result.category == result.CATEGORY_CORRECT:
                results.append(run_result)
            else:
                assert run_result.category in {
                    result.CATEGORY_ERROR,
                    result.CATEGORY_UNKNOWN,
                }
    else:
        start_index = 0
        index_increment = lambda run_result: 1  # noqa: E731
        if options.correct_only:
            results = [
                run_result for run_result in run_set_result.results
                if run_result.category == result.CATEGORY_CORRECT
            ]
        else:
            results = run_set_result.results

    # sort data for quantile plot
    results.sort(key=get_extract_value_function(options.column_identifier))

    # extract information which id columns should be shown
    for run_result in run_set_result.results:
        run_result.id = run_result.task_id
    relevant_id_columns = tablegenerator.select_relevant_id_columns(results)

    # write output
    index = start_index
    for run_result in results:
        index += index_increment(run_result)
        task_ids = (
            task_id
            for task_id, show in zip(run_result.id, relevant_id_columns)
            if show)
        result_values = (util.remove_unit(value or "")
                         for value in run_result.values)
        print(*itertools.chain([index], task_ids, result_values), sep="\t")
Example #4
0
def _get_column_type_heur(column, column_values):
    if column.title == "status":
        return ColumnType.status

    column_type = column.type or None
    if column_type and column_type.type == ColumnType.measure:
        column_type = ColumnMeasureType(0)
    column_unit = column.unit  # May be None
    column_source_unit = column.source_unit  # May be None
    column_scale_factor = column.scale_factor  # May be None

    column_max_int_digits = 0
    column_max_dec_digits = 0
    column_has_numbers = False
    column_has_decimal_numbers = False

    if column_unit:
        explicit_unit_defined = True
    else:
        explicit_unit_defined = False

    if column_scale_factor is None:
        explicit_scale_defined = False
    else:
        explicit_scale_defined = True

    for value in column_values:

        if value is None or value == "":
            continue

        value_match = REGEX_MEASURE.match(str(value))

        # As soon as one row's value is no number, the column type is 'text'
        if value_match is None:
            return ColumnType.text
        else:
            column_has_numbers = True
            curr_column_unit = value_match.group(GROUP_UNIT)

            # If the units in two different rows of the same column differ,
            # 1. Raise an error if an explicit unit is defined by the displayUnit attribute
            #    and the unit in the column cell differs from the defined sourceUnit, or
            # 2. Handle the column as 'text' type, if no displayUnit was defined for the column's values.
            #    In that case, a unit different from the definition of sourceUnit does not lead to an error.
            if curr_column_unit:
                if column_source_unit is None and not explicit_scale_defined:
                    column_source_unit = curr_column_unit
                elif column_source_unit != curr_column_unit:
                    raise util.TableDefinitionError(
                        "Attribute sourceUnit different from real source unit: {} and {} (in column {})"
                        .format(column_source_unit, curr_column_unit,
                                column.title))
                if column_unit and curr_column_unit != column_unit:
                    if explicit_unit_defined:
                        _check_unit_consistency(curr_column_unit,
                                                column_source_unit, column)
                    else:
                        return ColumnType.text
                else:
                    column_unit = curr_column_unit

            if column_scale_factor is None:
                column_scale_factor = _get_scale_factor(
                    column_unit, column_source_unit, column)

            # Compute the number of decimal digits of the current value, considering the number of significant
            # digits for this column.
            # Use the column's scale factor for computing the decimal digits of the current value.
            # Otherwise, they might be different from output.
            scaled_value = float(util.remove_unit(
                str(value))) * column_scale_factor

            # Due to the scaling operation above, floats in the exponent notation may be created. Since this creates
            # special cases, immediately convert the value back to decimal notation.
            if value_match.group(GROUP_DEC_PART):
                # -1 since GROUP_DEC_PART includes the decimal point
                dec_digits_before_scale = len(
                    value_match.group(GROUP_DEC_PART)) - 1
            else:
                dec_digits_before_scale = 0
            max_number_of_dec_digits_after_scale = max(
                0, dec_digits_before_scale - ceil(log10(column_scale_factor)))

            scaled_value = "{0:.{1}f}".format(
                scaled_value, max_number_of_dec_digits_after_scale)
            scaled_value_match = REGEX_MEASURE.match(scaled_value)

            curr_dec_digits = _get_decimal_digits(
                scaled_value_match, column.number_of_significant_digits)
            column_max_dec_digits = max(column_max_dec_digits, curr_dec_digits)

            curr_int_digits = _get_int_digits(scaled_value_match)
            column_max_int_digits = max(column_max_int_digits, curr_int_digits)

            if (scaled_value_match.group(GROUP_DEC_PART) is not None
                    or value_match.group(GROUP_DEC_PART) is not None
                    or scaled_value_match.group(GROUP_SPECIAL_FLOATS_PART)
                    is not None):
                column_has_decimal_numbers = True

    if not column_has_numbers:
        # only empty values
        return ColumnType.text

    if (column_has_decimal_numbers or column_max_dec_digits
            or int(column_scale_factor) !=
            column_scale_factor  # non-int scaling factor
        ):
        column_type = ColumnMeasureType(column_max_dec_digits)
    else:
        column_type = ColumnType.count

    column_width = column_max_int_digits
    if column_max_dec_digits:
        column_width += column_max_dec_digits + 1

    return (
        column_type,
        column_unit,
        column_source_unit,
        column_scale_factor,
        column_width,
    )
Example #5
0
    def format_value(self, value, format_target):
        """
        Format a value nicely for human-readable output (including rounding).

        @param value: the value to format
        @param format_target the target the value should be formatted for
        @return: a formatted String representation of the given value.
        """
        # Only format counts and measures
        if self.type.type != ColumnType.count and self.type.type != ColumnType.measure:
            return value

        if format_target not in POSSIBLE_FORMAT_TARGETS:
            raise ValueError("Unknown format target")

        if value is None or value == "":
            return ""

        if isinstance(value, str):
            # If the number ends with "s" or another unit, remove it.
            # Units should not occur in table cells, but in the table head.
            number_str = util.remove_unit(value.strip())
            number = Decimal(number_str)
        elif isinstance(value, Decimal):
            number = value
            number_str = util.print_decimal(number)
        else:
            raise TypeError("Unexpected number type " + str(type(value)))

        if number.is_nan():
            return "NaN"
        elif number == inf:
            return "Inf"
        elif number == -inf:
            return "-Inf"

        # Apply the scale factor to the value
        if self.scale_factor is not None:
            number *= self.scale_factor
        assert number.is_finite()

        if (self.number_of_significant_digits is None
                and self.type.type != ColumnType.measure
                and format_target == "tooltip_stochastic"):
            # Column of type count (integral values) without specified sig. digits.
            # However, we need to round values like stdev, so we just round somehow.
            return util.print_decimal(round(number, DEFAULT_TOOLTIP_PRECISION))

        number_of_significant_digits = self.get_number_of_significant_digits(
            format_target)
        max_dec_digits = (self.type.max_decimal_digits
                          if self.type.type == ColumnType.measure else 0)

        if number_of_significant_digits is not None:
            current_significant_digits = _get_significant_digits(number_str)
            return _format_number(
                number,
                current_significant_digits,
                number_of_significant_digits,
                max_dec_digits,
                format_target,
            )
        else:
            return util.print_decimal(number)