Source code for polynomials_on_simplices.generic_tools.str_utils

"""
Utility functions for generating math-related strings.
"""

import fractions


[docs]def str_number(a, latex_fraction=False, prettify_fractions=True, n=10): r""" Convert a number to string. :param a: Number we want to convert to string. :param bool latex_fraction: If the number is a fraction, whether or not to encode the fractions using the Latex "\frac" syntax. Default is to just use '/' for the fraction. :param bool prettify_fractions: Whether or not to convert a number which is a 'nice' fraction (see :func:`is_nice_fraction`) to its fraction representation. :param int n: How large the denominator can be in a fraction for the fraction to be considered nice. Only used when `prettify_fractions` is True. :return: String representation of the number. :rtype: str .. rubric:: Examples >>> str_number(1.0) '1' >>> str_number(0.0) '0' >>> str_number(0.5) '1 / 2' >>> str_number(0.5, prettify_fractions=False) '0.5' """ if _is_integer(a): return str(int(a)) if prettify_fractions: if is_nice_fraction(a, n): return convert_float_to_fraction(a, latex_fraction) return str(a)
[docs]def str_number_array(a, latex=False, latex_column_vector=True, latex_array_style="pmatrix"): r""" Convert an array of numbers to string. :param a: Array of numbers we want to convert to string. :param bool latex: Whether or not to encode the array using Latex syntax. :param bool latex_column_vector: Whether the array should be encoded as a row or column vector. Only applicable if `latex` is true. :param latex_array_style: Latex array style to use. Only applicable if `latex` is true. :return: String representation of the array of numbers. :rtype: str .. rubric:: Examples >>> str_number_array([1.0, 2.0]) '[1, 2]' >>> str_number_array([0.0, 0.5], latex=True) '\\begin{pmatrix}0 \\\\ \\frac{1}{2}\\end{pmatrix}' >>> str_number_array([3, 4], latex=True, latex_column_vector=False, latex_array_style="bmatrix") '\\begin{bmatrix}3 & 4\\end{bmatrix}' """ if latex: res = "\\begin{" + latex_array_style + "}" if latex_column_vector: res += " \\\\ ".join(str_number(n, latex_fraction=True) for n in a) else: res += " & ".join(str_number(n, latex_fraction=True) for n in a) res += "\\end{" + latex_array_style + "}" return res else: return "[" + ", ".join(str_number(n) for n in a) + "]"
[docs]def is_nice_fraction(f, n=10): """ Check if a scalar can be expressed as a 'nice' rational number. Nice in this sense means that it can be written as a fraction with a denominator less than a given number n. :param float f: Scalar value we want to check. :param int n: How large the denominator can be in the fraction for the fraction to be considered nice. :return: Whether or not the scalar is a 'nice' fraction. :rtype: bool .. rubric:: Examples >>> is_nice_fraction(0.1) True >>> is_nice_fraction(0.01) False >>> is_nice_fraction(0.01, 100) True """ r = fractions.Fraction(f).limit_denominator(n) return abs(r - f) < 1e-10
[docs]def convert_float_to_fraction(s, latex_fraction=False): r""" Convert a float in string format to a fraction. :param str s: Float value as a string. :param bool latex_fraction: Whether or not to encode fractions using the Latex "\frac" syntax. Default is to just use '/' for the fraction. :return: Fraction string. .. rubric:: Examples >>> convert_float_to_fraction('0.5') '1 / 2' >>> convert_float_to_fraction('0') '0' >>> convert_float_to_fraction('0.5', latex_fraction=True) '\\frac{1}{2}' >>> convert_float_to_fraction('0', latex_fraction=True) '0' >>> convert_float_to_fraction('-0.5', latex_fraction=True) '-\\frac{1}{2}' >>> convert_float_to_fraction('2', latex_fraction=True) '2' """ f = float(s) r = fractions.Fraction(f).limit_denominator(1000) if r.numerator == 0: return '0' if r.denominator == 1: return str(r.numerator) if latex_fraction: if r.numerator < 0: return r"-\frac{" + str(-r.numerator) + "}{" + str(r.denominator) + "}" else: return r"\frac{" + str(r.numerator) + "}{" + str(r.denominator) + "}" else: return str(r.numerator) + " / " + str(r.denominator)
[docs]def str_sum(a, b): r""" Generate the string for the sum of two values, given by strings. :param str a: First value in the sum. :param str b: Second value in the sum. :return: String for the sum of the two values. :rtype: str .. rubric:: Examples >>> str_sum("a1", "a2") 'a1 + a2' >>> str_sum("a1", "-a2") 'a1 - a2' >>> str_sum("a1", "0") 'a1' >>> str_sum("0", "-a2") '-a2' >>> str_sum("-a1", "1") '-a1 + 1' """ if a == "0": return b if b == "0": return a if b.startswith("-"): return a + " - " + b[1:] else: return a + " + " + b
[docs]def str_multi_sum(a): r""" Generate the string for the sum of an array of values, given by strings. :param List[str] a: Values in the sum. :return: String for the sum of the values in the array. :rtype: str .. rubric:: Examples >>> str_multi_sum(["a1", "a2"]) 'a1 + a2' >>> str_multi_sum(["a1", "-a2"]) 'a1 - a2' >>> str_multi_sum(["a1", "0", "a3"]) 'a1 + a3' >>> str_multi_sum(["0", "-a2", "a3"]) '-a2 + a3' """ res = "0" for value in a: res = str_sum(res, value) return res
[docs]def str_product(a, b, multiplication_character=""): r""" Generate the string for the product of two values, given by strings. :param str a: First value in the product. :param str b: Second value in the product. :param str multiplication_character: Character(s) used to represent the product operator. :return: String for the product of the two values. :rtype: str .. rubric:: Examples >>> str_product("a1", "a2") 'a1 a2' >>> str_product("a1", "-a2", multiplication_character="\\times") '-a1 \\times a2' >>> str_product("a1", "0") '0' >>> str_product("a1", "1") 'a1' """ if a == "0" or b == "0": return "0" if a == "1": return b if b == "1": return a if a.startswith("-"): if b.startswith("-"): return str_product(a[1:], b[1:], multiplication_character) else: return "-" + str_product(a[1:], b, multiplication_character) else: if b.startswith("-"): return "-" + str_product(a, b[1:], multiplication_character) else: if multiplication_character != "": return a + " " + multiplication_character + " " + b else: return a + " " + b
[docs]def str_multi_product(a, multiplication_character=""): r""" Generate the string for the product of an array of values, given by strings. :param List[str] a: Values in the product. :param str multiplication_character: Character(s) used to represent the product operator. :return: String for the product of the array of values. :rtype: str .. rubric:: Examples >>> str_multi_product(["a1", "a2", "a3"]) 'a1 a2 a3' >>> str_multi_product(["a1", "-a2"], multiplication_character="\\times") '-a1 \\times a2' >>> str_multi_product(["a1", "0", "a3"]) '0' >>> str_multi_product(["a1", "1", "a3"]) 'a1 a3' """ res = "1" for value in a: res = str_product(res, value, multiplication_character) return res
[docs]def str_exponent(base, exp, str_type="python"): r""" Generate the string for the power of one value raised to an exponent, given by strings. :param str base: Base value. :param str exp: Exponent value. :param str str_type: Type of string, 'python' or 'latex'. Determines the exponentiation character used, '**' or '^'. Also for the 'latex' string type the exponent is wrapped in braces ({}) if it's longer than one character. :return: String for the base raised to the power of the exponent. :rtype: str >>> str_exponent("e", "x") 'e**x' >>> str_exponent("e", "x", str_type="latex") 'e^x' >>> str_exponent("e", "x_2", str_type="latex") 'e^{x_2}' >>> str_exponent("e", "0") '1' >>> str_exponent("e", "1") 'e' >>> str_exponent("0", "0") '1' """ if base == "0": if exp == "0": return "1" else: return "0" if exp == "0": return "1" if exp == "1": return base if str_type == "python": return base + "**" + exp else: if len(exp) == 1: return base + "^" + exp else: return base + "^{" + exp + "}"
[docs]def str_dot_product(a, b, multiplication_character=""): r""" Generate the string for a dot product of two arrays, with array elements given by strings. :param List[str] a: First array of elements in the dot product. :param List[str] b: Second array of elements in the dot product. :param str multiplication_character: Character(s) used to represent the product operator. :return: String for the dot product of the two arrays. :rtype: str .. rubric:: Examples >>> str_dot_product(["a1", "a2"], ["b1", "b2"]) 'a1 b1 + a2 b2' >>> str_dot_product(["a1", "a2"], ["b1", "b2"], multiplication_character="\\cdot") 'a1 \\cdot b1 + a2 \\cdot b2' """ try: len(a) assert len(a) == len(b) except TypeError: return str_product(a, b, multiplication_character) res = "0" for i in range(len(a)): p = str_product(a[i], b[i], multiplication_character) res = str_sum(res, p) return res
[docs]def split_long_str(string, sep=" ", long_str_threshold=-1, sep_replacement_before="", sep_replacement_after=""): """ Split a long string into an array of parts. The string will be split at occurrences of the given separator in a way that makes sure that each part is shorter than the given threshold length. :param str string: String we want to split. :param str sep: The delimiter according which to split the string. :param int long_str_threshold: Don't split at a delimiter if the resulting parts will be shorter than this threshold. The default value -1 indicates that we should always split at a delimiter. :param str sep_replacement_before: When splitting at a separator, the separator will be replaced with this string in the part before the split. :param str sep_replacement_after: When splitting at a separator, the separator will be replaced with this string in the part after the split. :return: Parts of the original string after the split. :rtype: List[str] .. rubric Examples >>> split_long_str("Hello world", long_str_threshold=1) ['Hello', 'world'] >>> split_long_str("Hello world", long_str_threshold=15) ['Hello world'] >>> split_long_str("My quite long sentence example", long_str_threshold=15) ['My quite long', 'sentence', 'example'] >>> split_long_str("Hello world", sep_replacement_before="_") ['Hello_', 'world'] >>> split_long_str("Hello world", sep_replacement_after="_") ['Hello', '_world'] """ potential_parts = string.split(sep) if long_str_threshold == -1: if sep_replacement_before != "": for i in range(0, len(potential_parts) - 1): potential_parts[i] += sep_replacement_before if sep_replacement_after != "": for i in range(1, len(potential_parts)): potential_parts[i] = sep_replacement_after + potential_parts[i] return potential_parts parts = [] part = potential_parts[0] for i in range(1, len(potential_parts)): if len(part + sep + potential_parts[i] + sep_replacement_before) < long_str_threshold: part += sep + potential_parts[i] else: parts.append(part + sep_replacement_before) part = sep_replacement_after + potential_parts[i] parts.append(part) return parts
def _latex_braces_wrapped_number(a): if len(a) == 1: return a return "{" + a + "}"
[docs]def str_sequence(symbol, n, indexing="fortran", index_style="inline"): """ Generate a sequence of enumerated strings, e.g. (a1, a2, a3). :param str symbol: Base symbol(s) to use for each string. :param int n: Number of strings to generate. :param indexing: Which indexing to use. Can be "c" for 0-based indexing (0, 1, 2, ...), or "fortran" for 1-based indexing (1, 2, 3, ...). :param index_style: How indices should be added to the base symbol(s). Can be any of "inline", "subscript", "superscript", "latex_subscript", "latex_superscript" or "list". See examples below. The "latex_subscript" differ from the "subscript" variant in that the index is wrapped in braces ({}) if it's longer than one character, and similarly for the "latex_superscript" variant. :return: List of strings. .. rubric:: Examples >>> str_sequence('a', 3) ['a1', 'a2', 'a3'] >>> str_sequence('a', 3, indexing="c") ['a0', 'a1', 'a2'] >>> str_sequence('a', 3, index_style="subscript") ['a_1', 'a_2', 'a_3'] >>> str_sequence('a', 3, index_style="superscript") ['a^1', 'a^2', 'a^3'] >>> str_sequence('a', 10, index_style="latex_subscript") ['a_1', 'a_2', 'a_3', 'a_4', 'a_5', 'a_6', 'a_7', 'a_8', 'a_9', 'a_{10}'] >>> str_sequence('a', 3, index_style="list") ['a[1]', 'a[2]', 'a[3]'] """ assert n >= 0, "Sequence length cannot be negative" if n == 0: return [] if indexing == "fortran": r = range(1, n + 1) else: r = range(n) if index_style == "inline": return [str(symbol) + str(i) for i in r] if index_style == "subscript": return [str(symbol) + "_" + str(i) for i in r] if index_style == "superscript": return [str(symbol) + "^" + str(i) for i in r] if index_style == "latex_subscript": return [str(symbol) + "_" + _latex_braces_wrapped_number(str(i)) for i in r] if index_style == "latex_superscript": return [str(symbol) + "^" + _latex_braces_wrapped_number(str(i)) for i in r] if index_style == "list": return [str(symbol) + "[" + str(i) + "]" for i in r]
def _is_integer(f): """ Check if a scalar is an integer. :param f: Scalar that we want to check. :return: Whether or not the scalar is an integer. :rtype: bool """ return f == int(f)