moptipy.utils package

Utilities used in other moptipy modules.

Submodules

moptipy.utils.formatted_string module

Strings that carry format information.

class moptipy.utils.formatted_string.FormattedStr(value, bold: bool = False, italic: bool = False, code: bool = False, mode: int = 0)[source]

Bases: str

A subclass of str capable of holding formatting information.

This is a version of string that also stores format information that can, for example, be used by a text format driver when typesetting text. Instances of this class can also be used as normal strings and be printed to the console without any issue. However, they also hold information about whether the text should be bold, italic, or rendered in a monospace code font.

Furthermore, if a number or numerical string is represented as a formatted string, the field mode will be non-zero. If it is TEXT=1, the string is a normal number, if it is NAN=2, the string is “nan”, if it is POSITIVE_INFINITY=3, then the string is inf, and if it is NEGATIVE_INFINITY=4, the string is -inf. These values permit a text driver, for example, to replace the special numeric values with unicode constants or certain commands. It may also choose to replace floating point number of the form 1.5E23 with something like 1.5*10^23. It can do so because a non-zero mode indicates that the string is definitely representing a number and that numbers in the string did not just occur for whatever other reason.

If the field mode has value SPECIAL=5, then the text contains a special sequence, e.g., a unicode character outside of the normal range. Then, the text format driver can render this character as a special entity.

static add_format(s, bold=False, italic=False, code=False)[source]

Add the given format to the specified string.

Parameters:
  • s (str) – the string

  • bold (bool, default: False) – should the format be bold face?

  • italic (bool, default: False) – should the format be italic face?

  • code (bool, default: False) – should the format be code face?

Return type:

str

>>> from typing import cast
>>> st = "abc"
>>> type(st)
<class 'str'>
>>> fs = cast(FormattedStr, FormattedStr.add_format(st, bold=True))
>>> type(fs)
<class 'moptipy.utils.formatted_string.FormattedStr'>
>>> fs.bold
True
>>> fs.italic
False
>>> fs = cast(FormattedStr, FormattedStr.add_format(fs, italic=True))
>>> fs.bold
True
>>> fs.italic
True
>>> fs.mode
0
bold: bool

should this string be formatted in bold face?

code: bool

should this string be formatted in code face?

italic: bool

should this string be formatted in italic face?

mode: int

the special mode: TEXT, NUMBER, NAN, POSITIVE_INFINITY, NEGATIVE_INFINITY, or SPECIAL

static number(number)[source]

Create a formatted string representing a number.

Parameters:

number (int | float | str | None) – the original number or numeric string

Return type:

FormattedStr

Returns:

the formatted string representing it

>>> FormattedStr.number(inf)
'inf'
>>> FormattedStr.number(inf) is _PINF
True
>>> FormattedStr.number(inf).mode
3
>>> FormattedStr.number(-inf)
'-inf'
>>> FormattedStr.number(-inf) is _NINF
True
>>> FormattedStr.number(-inf).mode
4
>>> FormattedStr.number(nan)
'nan'
>>> FormattedStr.number(nan) is _NAN
True
>>> FormattedStr.number(nan).mode
2
>>> FormattedStr.number(123)
'123'
>>> FormattedStr.number(123e3)
'123000.0'
>>> FormattedStr.number(123e3).mode
1
static special(text)[source]

Create a special string.

A special string has mode SPECIAL set and notifies the text format that special formatting conversion, e.g., a translation from the character “alpha” (u03b1) to alpha is required.

Parameters:

text (str) – the text

Return type:

FormattedStr

>>> FormattedStr.special("bla")
'bla'
>>> FormattedStr.special("bla").mode
5
moptipy.utils.formatted_string.NAN: Final[int] = 2

the formatted string represents NaN, i.e., “not a number”

moptipy.utils.formatted_string.NEGATIVE_INFINITY: Final[int] = 4

the formatted string represents negative infinity

moptipy.utils.formatted_string.NUMBER: Final[int] = 1

the formatted string represents a number

moptipy.utils.formatted_string.POSITIVE_INFINITY: Final[int] = 3

the formatted string represents positive infinity

moptipy.utils.formatted_string.SPECIAL: Final[int] = 5

the formatted string represents a special character

moptipy.utils.formatted_string.TEXT: Final[int] = 0

the formatted string represents normal text

moptipy.utils.help module

Print a help screen.

moptipy.utils.help.moptipy_argparser(file, description, epilog)[source]

Create an argument parser with default settings.

Parameters:
  • file (str) – the __file__ special variable of the calling script

  • description (str) – the description string

  • epilog (str) – the epilogue string

Return type:

ArgumentParser

Returns:

the argument parser

>>> ap = moptipy_argparser(
...     __file__, "This is a test program.", "This is a test.")
>>> isinstance(ap, argparse.ArgumentParser)
True
>>> "Copyright" in ap.epilog
True

moptipy.utils.html module

The HTML text format driver.

class moptipy.utils.html.HTML[source]

Bases: TextFormatDriver

The HTML text driver.

begin_table_body(stream, cols)[source]

Write the beginning of the table body.

Return type:

None

begin_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

Begin an HTML table cell.

Return type:

None

begin_table_header(stream, cols)[source]

Begin the header of an HTML table.

Return type:

None

begin_table_row(stream, cols, section_index, row_index, row_mode)[source]

Begin a row in an HTML table.

Return type:

None

end_table_body(stream, cols)[source]

Write the ending of the table body.

Return type:

None

end_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

End an HTML table cell.

Return type:

None

end_table_header(stream, cols)[source]

End the header of an HTML table.

Return type:

None

end_table_row(stream, cols, section_index, row_index)[source]

End a row in a HTML table.

Return type:

None

static instance()[source]

Get the HTML format singleton instance.

Return type:

HTML

Returns:

the singleton instance of the HTML format

text(stream, text, bold, italic, code, mode)[source]

Print a text string.

Return type:

None

moptipy.utils.html.SPECIAL_CHARS: Final[dict[str, str]] = {'-inf': '-&#x221E;', '-∞': '-&#x221e;', 'inf': '&#x221E;', 'nan': '&#x2205;', 'α': '&#x3b1;', '—': '&mdash;', '∅': '&#x2205;', '∞': '&#x221e;'}

special characters in HTML

moptipy.utils.lang module

The Lang class provides facilities for easy internationalization.

The idea here is to have simply tools that provide locale-specific keywords, texts, and number formats.

moptipy.utils.lang.DE: Final[Lang] = de

the German language

moptipy.utils.lang.EN: Final[Lang] = en

the English language

class moptipy.utils.lang.Lang(name, font, decimal_stepwidth=3, data=None, is_default=False)[source]

Bases: object

A language-based dictionary for locale-specific keywords.

static all_langs()[source]

Get all presently loaded languages.

Return type:

Iterable[Lang]

Returns:

an Iterable of the languages

static current()[source]

Get the current language.

Return type:

Lang

Returns:

the current language

>>> Lang.get("en").set_current()
>>> print(Lang.current().filename("b"))
b
>>> Lang.get("zh").set_current()
>>> print(Lang.current().filename("b"))
b_zh
extend(data)[source]

Add a set of entries to this dictionary.

Parameters:

data (dict[str, str]) – the language-specific data

Return type:

None

filename(base)[source]

Make a suitable filename by appending the language id.

Parameters:

base (str) – the basename

Returns:

the filename

Return type:

str

>>> print(Lang.get("en").filename("test"))
test
>>> print(Lang.get("zh").filename("test"))
test_zh
font()[source]

Get the default font for this language.

Return type:

str

Returns:

the default font for this language

>>> print(Lang.get("en").font())
DejaVu Sans
>>> print(Lang.get("zh").font())
Noto Sans SC
format_int(value)[source]

Convert an integer to a string.

Parameters:

value (int) – the value

Return type:

str

Returns:

a string representation of the value

>>> print(Lang.get("en").format_int(100000))
100'000
>>> print(Lang.get("zh").format_int(100000))
10'0000
format_str(item, **kwargs)[source]

Return a string based on the specified format.

Parameters:
  • item (str) – the key

  • kwargs – the keyword-based arguments

Return type:

str

>>> l = Lang.get("en")
>>> l.extend({"z": "{a}: bla{b}"})
>>> print(l.format_str("z", a=5, b=6))
5: bla6
static get(name)[source]

Get the language of the given key.

Parameters:

name (str) – the language name

Return type:

Lang

Returns:

the language

set_current()[source]

Mark this language as the current one.

Return type:

None

static translate(key)[source]

Translate the given key to a string in the current language.

Parameters:

key (str) – the key

Return type:

str

Returns:

the value of the key in the current language

>>> EN.extend({'a': 'b'})
>>> EN.set_current()
>>> print(Lang.translate("a"))
b
>>> DE.extend({'a': 'c'})
>>> Lang.get("de").set_current()
>>> print(Lang.translate("a"))
c
static translate_call(key)[source]

Get a callable that always returns the current translation of a key.

The callable will ignore all of its parameters and just return the translation. This means that you can pass parameters to it and they will be ignored.

Parameters:

key (str) – the key to translate

Return type:

Callable

Returns:

the callable doing the translation

>>> cal = Lang.translate_call("a")
>>> EN.extend({'a': 'b'})
>>> EN.set_current()
>>> print(cal())
b
>>> print(cal(1, 2, 3))
b
>>> DE.extend({'a': 'c'})
>>> Lang.get("de").set_current()
>>> print(cal("x"))
c
static translate_func(func)[source]

Create a lambda taking a dimensions and presenting a function thereof.

Parameters:

func (str) – the function name

Return type:

Callable[[str], str]

Returns:

the function

>>> Lang.get("en").set_current()
>>> Lang.get("en").extend({"ERT": "ERT"})
>>> Lang.get("en").extend({"FEs": "time in FEs"})
>>> f = Lang.translate_func("ERT")
>>> print(f("FEs"))
ERT [time in FEs]
>>> Lang.get("de").set_current()
>>> Lang.get("de").extend({"ERT": "ERT"})
>>> Lang.get("de").extend({"FEs": "Zeit in FEs"})
>>> print(f("FEs"))
ERT [Zeit in FEs]
moptipy.utils.lang.ZH: Final[Lang] = zh

the Chinese language

moptipy.utils.latex module

The latex text format driver.

class moptipy.utils.latex.LaTeX[source]

Bases: TextFormatDriver

The LaTeX text driver.

>>> from io import StringIO
>>> from moptipy.utils.formatted_string import FormattedStr
>>> from moptipy.utils.table import Table
>>> s = StringIO()
>>> latex = LaTeX.instance()
>>> print(str(latex))
tex
>>> with Table(s, "lrc", latex) as t:
...     with t.header() as header:
...         with header.row() as h:
...             h.cell(FormattedStr("1", bold=True))
...             h.cell(FormattedStr("2", code=True))
...             h.cell(FormattedStr("3", italic=True))
...     with t.section() as g:
...         with g.row() as r:
...             r.cell("a")
...             r.cell("b")
...             r.cell("c")
...         with g.row() as r:
...             r.cell("d")
...             r.cell("e")
...             r.cell("f")
>>> print(f"'{s.getvalue()}'")
'\begin{tabular}{lrc}%
\hline%
{\textbf{1}}&{\texttt{2}}&{\textit{3}}\\%
\hline%
a&b&c\\%
d&e&f\\%
\hline%
\end{tabular}%
'
begin_table_body(stream, cols)[source]

Write the beginning of the table body.

Return type:

None

begin_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

Begin a LaTeX table cell.

Return type:

None

begin_table_header(stream, cols)[source]

Begin the header of a LaTeX table.

Return type:

None

end_table_body(stream, cols)[source]

Write the ending of the table body.

Return type:

None

end_table_header(stream, cols)[source]

End the header of a LaTeX table.

Return type:

None

end_table_row(stream, cols, section_index, row_index)[source]

End a row in a LaTeX table.

Return type:

None

end_table_section(stream, cols, section_index, n_rows)[source]

End a table section.

Return type:

None

end_table_section_header(stream, cols, section_index)[source]

End a table section header.

Return type:

None

static instance()[source]

Get the LaTeX format singleton instance.

Return type:

LaTeX

Returns:

the singleton instance of the LaTeX format

text(stream, text, bold, italic, code, mode)[source]

Print a text string.

Return type:

None

moptipy.utils.latex.SPECIAL_CHARS: Final[dict[str, str]] = {'-inf': '$-\\infty$', '-∞': '$-\\infty$', 'inf': '$\\infty$', 'nan': '$\\emptyset$', 'α': '$\\alpha$', '—': '---', '∅': '$\\emptyset$', '∞': '$\\infty$'}

special characters in LaTeX

moptipy.utils.logger module

Classes for writing structured log files.

A Logger offers functionality to write structured, text-based log files that can hold a variety of information. It is implemented in two flavors, FileLogger, which writes data to a file, and InMemoryLogger, which writes data to a buffer in memory (which is mainly useful for testing).

A Logger can produce output in three formats:

  • csv() creates a section of semicolon-separated-values data (CsvLogSection), which we call csv because it structured basically exactly as the well-known comma-separated-values data, just using semicolons.

  • key_values() creates a key-values section (KeyValueLogSection) in YAML format. The specialty of this section is that it permits hierarchically structuring of data by spawning out sub-sections that are signified by a key prefix via scope().

  • text() creates a raw text section (TextLogSection), into which raw text can be written.

The beginning and ending of section named XXX are BEGIN_XXX and END_XXX.

This class is used by the Execution and experiment-running facility (run_experiment()) to produce log files complying with https://thomasweise.github.io/moptipy/#log-files. Such log files can be parsed via log_parser.

class moptipy.utils.logger.CsvLogSection(title, logger, header)[source]

Bases: LogSection

A logger that is designed to output CSV data.

The coma-separated-values log is actually a semicolon-separated-values log. This form of logging is used to store progress information or time series data, as captured by the optimization Process and activated by, e.g., the methods set_log_improvements() and set_log_all_fes(). It will look like this:

>>> with InMemoryLogger() as logger:
...     with logger.csv("CSV", ["A", "B", "C"]) as csv:
...         csv.row((1, 2, 3))
...         csv.row([1.5, 2.0, 3.5])
...     print(logger.get_log())
['BEGIN_CSV', 'A;B;C', '1;2;3', '1.5;2;3.5', 'END_CSV']
row(row)[source]

Write a row of csv data.

Parameters:

row (tuple[int | float | bool, ...] | list[int | float | bool]) – the row of data

Return type:

None

class moptipy.utils.logger.FileLogger(path)[source]

Bases: Logger

A logger logging to a file.

class moptipy.utils.logger.InMemoryLogger[source]

Bases: Logger

A logger logging to a string in memory.

get_log()[source]

Obtain all the lines logged to this logger.

Return type:

list[str]

Returns:

a list of strings with the logged lines

moptipy.utils.logger.KEY_HEX_VALUE: Final[str] = '(hex)'

the hexadecimal version of a value

moptipy.utils.logger.KEY_VALUE_SEPARATOR: Final[str] = ': '

the YAML-conform separator between a key and a value

class moptipy.utils.logger.KeyValueLogSection(title, logger, prefix, done)[source]

Bases: LogSection

A logger for key-value pairs.

The key-values section XXX starts with the line BEGIN_XXX and ends with the line END_XXX. On every line in between, there is a key-value pair of the form key: value. Key-values sections support so-called scopes. Key-values pairs belong to a scope Y if the key starts with Y. followed by the actual key, e.g., a.g: 5 denotes that the key g of scope a has value 5. Such scopes can be arbitrarily nested: The key-value pair x.y.z: 2 denotes a key z in the scope y nested within scope x having the value 2. This system of nested scopes allows you to recursively invoke the method log_parameters_to() without worrying of key clashes. Just wrap the call to the log_parameters_to method of a sub-component into a unique scope. At the same time, this avoids the need of any more complex hierarchical data structures in our log files.

>>> with InMemoryLogger() as logger:
...     with logger.key_values("A") as kv:
...         kv.key_value("x", 1)
...         with kv.scope("b") as sc1:
...             sc1.key_value("i", "j")
...             with sc1.scope("c") as sc2:
...                 sc2.key_value("l", 5)
...         kv.key_value("y", True)
...     print(logger.get_log())
['BEGIN_A', 'x: 1', 'b.i: j', 'b.c.l: 5', 'y: T', 'END_A']
key_value(key, value, also_hex=False)[source]

Write a key-value pair.

Given key A and value B, the line A: B will be added to the log. If value (B) happens to be a floating point number, the value will also be stored in hexadecimal notation (float.hex()).

Parameters:
  • key (str) – the key

  • value – the value

  • also_hex (bool, default: False) – also store the value as hexadecimal version

Return type:

None

scope(prefix)[source]

Create a new scope for key prefixes.

KeyValueLogSection only allows you to store flat key-value pairs where each key must be unique. However, what do we do if we have two components of an algorithm that have parameters with the same name (key)? We can hierarchically nest KeyValueLogSection sections via prefix scopes. The idea is as follows: If one component has sub-components, instead of invoking their log_parameters_to() methods directly, which could lead to key-clashes, it will create a scope() for each one and then pass these scopes to their log_parameters_to(). Each scope basically appends a prefix and a “.” to the keys. If the prefixes are unique, this ensures that all prefix+”.”+keys are unique, too.

>>> from moptipy.utils.logger import InMemoryLogger
>>> with InMemoryLogger() as l:
...     with l.key_values("A") as kv:
...         kv.key_value("x", "y")
...         with kv.scope("b") as sc1:
...             sc1.key_value("x", "y")
...             with sc1.scope("c") as sc2:
...                 sc2.key_value("x", "y")
...         with kv.scope("d") as sc3:
...             sc3.key_value("x", "y")
...             with sc3.scope("c") as sc4:
...                sc4.key_value("x", "y")
...     print(l.get_log())
['BEGIN_A', 'x: y', 'b.x: y', 'b.c.x: y', 'd.x: y', 'd.c.x: y', 'END_A']
Parameters:

prefix (str) – the key prefix

Return type:

KeyValueLogSection

Returns:

the new logger

class moptipy.utils.logger.LogSection(title, logger)[source]

Bases: AbstractContextManager

An internal base class for logger sections.

comment(comment)[source]

Write a comment line.

A comment starts with # and is followed by text.

Parameters:

comment (str) – the comment to write

Return type:

None

>>> from moptipy.utils.logger import InMemoryLogger
>>> with InMemoryLogger() as l:
...     with l.text("A") as tx:
...         tx.write("aaaaaa")
...         tx.comment("hello")
...     print(l.get_log())
['BEGIN_A', 'aaaaaa', '# hello', 'END_A']
class moptipy.utils.logger.Logger(name, writer, closer=<function Logger.<lambda>>)[source]

Bases: AbstractContextManager

An abstract base class for logging data in a structured way.

There are two implementations of this, InMemoryLogger, which logs data in memory and is mainly there for testing and debugging, an FileLogger which logs to a text file and is to be used in experiments with moptipy.

csv(title, header)[source]

Create a log section for CSV data with ; as column separator.

The first line will be the headline with the column names.

Parameters:
  • title (str) – the title of the new section

  • header (list[str]) – the list of column titles

Return type:

CsvLogSection

Returns:

the new logger

>>> from moptipy.utils.logger import FileLogger
>>> from pycommons.io.temp import temp_file
>>> with temp_file() as t:
...     with FileLogger(str(t)) as l:
...         with l.csv("A", ["x", "y"]) as csv:
...             csv.row([1,2])
...             csv.row([3,4])
...             csv.row([4, 12])
...     text = open(str(t), "r").read().splitlines()
...     print(text)
['BEGIN_A', 'x;y', '1;2', '3;4', '4;12', 'END_A']
>>> import csv
>>> for r in csv.reader(text[1:5], delimiter=";"):
...     print(r)
['x', 'y']
['1', '2']
['3', '4']
['4', '12']
key_values(title)[source]

Create a log section for key-value pairs.

The contents of such a section will be valid YAML mappings, i.w., conform to https://yaml.org/spec/1.2/spec.html#mapping. This means they can be parsed with a YAML parser (after removing the section start and end marker, of course).

Parameters:

title (str) – the title of the new section

Return type:

KeyValueLogSection

Returns:

the new logger

>>> from pycommons.io.temp import temp_file
>>> with temp_file() as t:
...     with FileLogger(str(t)) as l:
...         with l.key_values("B") as kv:
...             kv.key_value("a", "b")
...             with kv.scope("c") as kvc:
...                 kvc.key_value("d", 12)
...                 kvc.key_value("e", True)
...             kv.key_value("f", 3)
...     text = open(str(t), "r").read().splitlines()
>>> print(text)
['BEGIN_B', 'a: b', 'c.d: 12', 'c.e: T', 'f: 3', 'END_B']
>>> import yaml
>>> dic = yaml.safe_load("\n".join(text[1:5]))
>>> print(list(dic.keys()))
['a', 'c.d', 'c.e', 'f']
text(title)[source]

Create a log section for unstructured text.

Parameters:

title (str) – the title of the new section

Return type:

TextLogSection

Returns:

the new logger

>>> from moptipy.utils.logger import InMemoryLogger
>>> with InMemoryLogger() as l:
...     with l.text("C") as tx:
...         tx.write("aaaaaa")
...         tx.write("bbbbb")
...         tx.write("\n")
...         tx.write("ccccc")
...     print(l.get_log())
['BEGIN_C', 'aaaaaa', 'bbbbb', '', 'ccccc', 'END_C']
class moptipy.utils.logger.PrintLogger[source]

Bases: Logger

A logger logging to stdout.

moptipy.utils.logger.SECTION_END: Final[str] = 'END_'

the indicator of the end of a log section

moptipy.utils.logger.SECTION_START: Final[str] = 'BEGIN_'

the indicator of the start of a log section

moptipy.utils.logger.SPECIAL_CHAR_REPLACEMENT: Final[str] = '_'

the replacement for special characters

class moptipy.utils.logger.TextLogSection(title, logger)[source]

Bases: LogSection

A logger for raw, unprocessed text.

Such a log section is used to capture the raw contents of the solutions discovered by the optimization Process. For this purpose, our system will use the method to_str() of the search and/or solution Space.

>>> with InMemoryLogger() as logger:
...     with logger.text("T") as txt:
...         txt.write("Hello World!")
...     print(logger.get_log())
['BEGIN_T', 'Hello World!', 'END_T']
moptipy.utils.logger.parse_key_values(lines)[source]

Parse a key_values() section’s text.

Parameters:

lines (Iterable[str]) – the lines with the key-values pairs

Return type:

dict[str, str]

Returns:

the dictionary with the

>>> from moptipy.utils.logger import InMemoryLogger
>>> with InMemoryLogger() as l:
...     with l.key_values("B") as kv:
...         kv.key_value("a", "b")
...         with kv.scope("c") as kvc:
...             kvc.key_value("d", 12)
...             kvc.key_value("e", True)
...         kv.key_value("f", 3)
...     txt = l.get_log()
>>> print(txt)
['BEGIN_B', 'a: b', 'c.d: 12', 'c.e: T', 'f: 3', 'END_B']
>>> dic = parse_key_values(txt[1:5])
>>> keys = list(dic.keys())
>>> keys.sort()
>>> print(keys)
['a', 'c.d', 'c.e', 'f']

moptipy.utils.markdown module

The markdown text format driver.

class moptipy.utils.markdown.Markdown[source]

Bases: TextFormatDriver

The markdown text driver.

>>> from io import StringIO
>>> from moptipy.utils.formatted_string import FormattedStr
>>> from moptipy.utils.table import Table
>>> s = StringIO()
>>> md = Markdown.instance()
>>> print(str(md))
md
>>> with Table(s, "lrc", md) as t:
...     with t.header() as header:
...         with header.row() as h:
...             h.cell(FormattedStr("1", bold=True))
...             h.cell(FormattedStr("2", code=True))
...             h.cell(FormattedStr("3", italic=True))
...     with t.section() as g:
...         with g.row() as r:
...             r.cell("a")
...             r.cell("b")
...             r.cell("c")
...         with g.row() as r:
...             r.cell("d")
...             r.cell("e")
...             r.cell("f")
>>> print(f"'{s.getvalue()}'")
'|**1**|`2`|*3*|
|:--|--:|:-:|
|a|b|c|
|d|e|f|
'
begin_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

Begin a Markdown table cell.

Return type:

None

begin_table_row(stream, cols, section_index, row_index, row_mode)[source]

Begin a row in a markdown table.

Return type:

None

end_table_header(stream, cols)[source]

End the header of a markdown table.

Return type:

None

end_table_row(stream, cols, section_index, row_index)[source]

End a row in a Markdown table.

Return type:

None

static instance()[source]

Get the markdown format singleton instance.

Return type:

Markdown

Returns:

the singleton instance of the Markdown format

text(stream, text, bold, italic, code, mode)[source]

Print a text string.

Return type:

None

moptipy.utils.markdown.SPECIAL_CHARS: Final[dict[str, str]] = {'-inf': '$-\\infty$', '-∞': '$-\\infty$', 'inf': '$\\infty$', 'nan': '$\\emptyset$', 'α': '$\\alpha$', '—': '&mdash;', '∅': '$\\emptyset$', '∞': '$\\infty$'}

the special chars

moptipy.utils.math module

Simple routines for handling numbers and numerical stuff.

moptipy.utils.math.DBL_INT_LIMIT_P: Final[float] = 9007199254740992.0

The positive limit for doubles that can be represented exactly as ints.

moptipy.utils.math.try_float_div(a, b)[source]

Try to divide two numbers at best precision.

First, we will check if we can convert the numbers to integers without loss of precision via try_int(). If yes, then we go for the maximum-precision integer division via try_int_div(). If no, then we do the normal floating point division and try to convert the result to an integer if that can be done without loss of precision.

Parameters:
Return type:

int | float

Returns:

a/b, but always finite

Raises:

ValueError – if either one of the arguments or the final result would not be finite

>>> try_float_div(1e180, 1e60)
1.0000000000000001e+120
>>> try_float_div(1e60, 1e-60)
1e+120
>>> try_float_div(1e14, 1e-1)
1000000000000000
>>> try_float_div(1e14, -1e-1)
-1000000000000000
>>> try_float_div(-1e14, 1e-1)
-1000000000000000
>>> try_float_div(-1e14, -1e-1)
1000000000000000
>>> try_float_div(1e15, 1e-1)
1e+16
>>> try_float_div(1e15, -1e-1)
-1e+16
>>> try_float_div(-1e15, 1e-1)
-1e+16
>>> try_float_div(-1e15, -1e-1)
1e+16
>>> try_float_div(1e15, 1e-15)
9.999999999999999e+29
>>> print(type(try_float_div(10, 2)))
<class 'int'>
>>> print(type(try_float_div(10, 3)))
<class 'float'>
>>> print(type(try_float_div(10, 0.5)))
<class 'int'>
>>> from math import inf, nan
>>> try:
...     try_float_div(1.0, 0)
... except ZeroDivisionError as zde:
...     print(zde)
integer division or modulo by zero
>>> try:
...     try_float_div(1.0, -0.0)
... except ZeroDivisionError as zde:
...     print(zde)
integer division or modulo by zero
>>> try:
...     try_float_div(inf, 0)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is inf.
>>> try:
...     try_float_div(-inf, 0)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is -inf.
>>> try:
...     try_float_div(nan, 0)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is nan.
>>> try:
...     try_float_div(1, inf)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is inf.
>>> try:
...     try_float_div(1, -inf)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is -inf.
>>> try:
...     try_float_div(1, nan)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is nan.
>>> try:
...     try_float_div(1e300, 1e-60)
... except ValueError as ve:
...     print(ve)
Result must be finite, but is 1e+300/1e-60=inf.
moptipy.utils.math.try_int(val)[source]

Attempt to convert a float to an integer.

This method will convert a floating point number to an integer if the floating point number was representing an exact integer. This is the case if it has a) no fractional part and b) is in the range -9007199254740992…9007199254740992, i.e., the range where +1 and -1 work without loss of precision.

Parameters:

val (int | float) – the input value, which must either be int or float

Return type:

int | float

Returns:

an int if val can be represented as int without loss of precision, val otherwise

Raises:
  • TypeError – if val is neither an instance of int nor of float

  • ValueError – if val is a float, but not finite

>>> print(type(try_int(10.5)))
<class 'float'>
>>> print(type(try_int(10)))
<class 'int'>
>>> from math import inf, nan, nextafter
>>> type(try_int(0.0))
<class 'int'>
>>> type(try_int(0.5))
<class 'float'>
>>> try:
...     try_int(inf)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is inf.
>>> try:
...     try_int(-inf)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is -inf.
>>> try:
...     try_int(nan)
... except ValueError as ve:
...     print(ve)
Value must be finite, but is nan.
>>> try:
...     try_int("blab")  # noqa  # type: off
... except TypeError as te:
...     print(te)
val should be an instance of any in {float, int} but is str, namely 'blab'.
>>> type(try_int(9007199254740992.0))
<class 'int'>
>>> try_int(9007199254740992.0)
9007199254740992
>>> too_big = nextafter(9007199254740992.0, inf)
>>> print(too_big)
9007199254740994.0
>>> type(try_int(too_big))
<class 'float'>
>>> type(try_int(-9007199254740992.0))
<class 'int'>
>>> try_int(-9007199254740992.0)
-9007199254740992
>>> type(try_int(-too_big))
<class 'float'>
moptipy.utils.math.try_int_div(a, b)[source]

Try to divide two integers at best precision.

Floating point divisions can incur some loss of precision. We try to avoid this here as much as possible. First, we check if a is divisible by b without any fractional part. If this is true, then we can do a pure integer division without loss of any precision.

Otherwise, we would do a floating point division. This will lead the values be converted to floating point numbers, which can incur a loss of precision. In the past, we tried to divide both numbers by their gcd to make them smaller in order to avoid a loss of precision. But it seems that Python is already doing something like this internally when performing the a / b division anyway, so we don’t do this anymore. However, it is still attempted to convert the result back to an integer.

Parameters:
  • a (int) – the first integer

  • b (int) – the second integer

Return type:

int | float

Returns:

a/b, either as int or as float but always a finite value

Raises:
  • OverflowError – if the division must be performed using floating point arithmetic, but the result would be too large to fit into a float

  • ZeroDivisionError – if b==0

>>> print(try_int_div(10, 2))
5
>>> print(try_int_div(10, -2))
-5
>>> print(try_int_div(-10, 2))
-5
>>> print(try_int_div(-10, -2))
5
>>> print(type(try_int_div(10, 2)))
<class 'int'>
>>> print(try_int_div(10, 3))
3.3333333333333335
>>> print(try_int_div(-10, 3))
-3.3333333333333335
>>> print(try_int_div(10, -3))
-3.3333333333333335
>>> print(try_int_div(-10, -3))
3.3333333333333335
>>> print(type(try_int_div(10, 3)))
<class 'float'>
>>> print(try_int_div(9007199254740992, 1))
9007199254740992
>>> print(try_int_div(2109792310235001520128, 234234))
9007199254740992
>>> print(try_int_div(2109792310235001520128, 234235))
9007160801054503
>>> print(try_int_div(2109792310235001520128, 234233))
9007237708755818.0
>>> large = 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
>>> try:
...     large / 1
... except OverflowError as oe:
...     print(oe)
integer division result too large for a float
>>> try_int_div(large, 1)
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
>>> try_int_div(large * 7, 1 * 7)
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
>>> try:
...     try_int_div(large, 7)
... except OverflowError as oe:
...     print(oe)
integer division result too large for a float
>>> try:
...     try_int_div(0, 0)
... except ZeroDivisionError as zde:
...     print(zde)
integer division or modulo by zero
>>> try:
...     try_int_div(1, 0)
... except ZeroDivisionError as zde:
...     print(zde)
integer division or modulo by zero
>>> try:
...     try_int_div(-1, 0)
... except ZeroDivisionError as zde:
...     print(zde)
integer division or modulo by zero
moptipy.utils.math.try_int_root(value, power, none_on_overflow=True)[source]

Compute value**(1/power) where value and power are both integers.

Parameters:
  • value (int) – the integer value to compute the root of

  • power (int) – the integer power of the root

  • none_on_overflow (bool, default: True) – True if None should be returned if we encounter an OverflowError, False if we should simply re-raise it

Return type:

int | float | None

Returns:

the root, or None if we encounter an overflow

>>> try_int_root(100, 2)
10
>>> try_int_root(100, 3) - (100 ** (1/3))
0.0
>>> try_int_root(123100, 23) - (123100 ** (1/23))
0.0
>>> 123100**1000 - try_int_root(123100**1000, 1000)**1000
0
>>> 11**290 - try_int_root(11**290, 290)**290
0
>>> (abs(1.797E308 - try_int_root(int(1.797E308), 10) ** 10)
...     < abs(1.797E308 - (int(1.797E308) ** 0.1) ** 10))
True

moptipy.utils.nputils module

Utilities for interaction with numpy.

moptipy.utils.nputils.DEFAULT_BOOL: Final[dtype] = dtype('bool')

The default boolean type.

moptipy.utils.nputils.DEFAULT_FLOAT: Final[dtype] = dtype('float64')

The default floating point type.

moptipy.utils.nputils.DEFAULT_INT: Final[dtype] = dtype('int64')

The default integer type: the signed 64-bit integer.

moptipy.utils.nputils.DEFAULT_NUMERICAL: Final[tuple[dtype, ...]] = (dtype('int8'), dtype('uint8'), dtype('int16'), dtype('uint16'), dtype('int32'), dtype('uint32'), dtype('int64'), dtype('uint64'), dtype('float64'))

The default numerical types.

moptipy.utils.nputils.DEFAULT_UNSIGNED_INT: Final[dtype] = dtype('uint64')

The default unsigned integer type: an unsigned 64-bit integer.

moptipy.utils.nputils.INTS: Final[tuple[dtype, ...]] = (dtype('int8'), dtype('uint8'), dtype('int16'), dtype('uint16'), dtype('int32'), dtype('uint32'), dtype('int64'), dtype('uint64'))

The numpy integer data types.

moptipy.utils.nputils.KEY_NUMPY_TYPE: Final[str] = 'dtype'

the character identifying the numpy data type backing the space

moptipy.utils.nputils.array_to_str(data)[source]

Convert a numpy array to a string.

This method represents a numpy array as a string. It makes sure to include all the information stored in the array and to represent it as compactly as possible.

If the array has numerical values, it will use the default CSV separator. If the array contains Boolean values, it will use no separator at all.

Parameters:

data (ndarray) – the data

Return type:

str

Returns:

the string

>>> import numpy as npx
>>> array_to_str(npx.array([1, 2, 3]))
'1;2;3'
>>> array_to_str(npx.array([1, 2.2, 3]))
'1;2.2;3'
>>> array_to_str(npx.array([True, False, True]))
'TFT'
moptipy.utils.nputils.dtype_for_data(always_int, lower_bound, upper_bound)[source]

Obtain the most suitable numpy data type to represent the data.

If the data is always integer, the smallest possible integer type will be sought using int_range_to_dtype(). If always_int is True and one or both of the bounds are infinite, then the largest available integer type is returned. If the bounds are finite but exceed the integer range, a ValueError is thrown. If the data is not always integer, the float64 is returned.

Parameters:
  • always_int (bool) – is the data always integer?

  • lower_bound (int | float) – the lower bound of the data, set to -inf if no lower bound is known and we should assume the full integer range

  • upper_bound (int | float) – the upper bound of the data, set to -inf if no upper bound is known and we should assume the full integer range

Raises:
  • ValueError – if the lower_bound > upper_bound or any bound is nan or the integer bounds exceed the largest int range

  • TypeError – if, well, you provide parameters of the wrong types

Return type:

dtype

>>> print(dtype_for_data(True, 0, 127))
int8
>>> print(dtype_for_data(True, 0, 128))
uint8
>>> print(dtype_for_data(True, -1, 32767))
int16
>>> print(dtype_for_data(True, 0, 32768))
uint16
>>> print(dtype_for_data(True, -1, 32768))
int32
>>> print(dtype_for_data(True, 0, 65535))
uint16
>>> print(dtype_for_data(True, 0, 65536))
int32
>>> print(dtype_for_data(True, -1, 65535))
int32
>>> print(dtype_for_data(True, 0, (2 ** 31) - 1))
int32
>>> print(dtype_for_data(True, 0, 2 ** 31))
uint32
>>> print(dtype_for_data(True, -1, 2 ** 31))
int64
>>> print(dtype_for_data(True, 0, (2 ** 63) - 1))
int64
>>> print(dtype_for_data(True, 0, 2 ** 63))
uint64
>>> print(dtype_for_data(True, 0, (2 ** 63) + 1))
uint64
>>> print(dtype_for_data(True, 0, (2 ** 64) - 1))
uint64
>>> try:
...     dtype_for_data(True, 0, 2 ** 64)
... except ValueError as v:
...     print(v)
max_value for unsigned integers must be <=18446744073709551615, but is 18446744073709551616 for min_value=0.
>>> from math import inf, nan
>>> print(dtype_for_data(True, 0, inf))
uint64
>>> print(dtype_for_data(True, -1, inf))
int64
>>> print(dtype_for_data(True, -inf, inf))
int64
>>> print(dtype_for_data(True, -inf, inf))
int64
>>> try:
...     dtype_for_data(True, 1, 0)
... except ValueError as v:
...     print(v)
invalid bounds [1,0].
>>> try:
...     dtype_for_data(False, 1, 0)
... except ValueError as v:
...     print(v)
invalid bounds [1,0].
>>> try:
...     dtype_for_data(True, 1, nan)
... except ValueError as v:
...     print(v)
invalid bounds [1,nan].
>>> try:
...     dtype_for_data(False, nan, 0)
... except ValueError as v:
...     print(v)
invalid bounds [nan,0].
>>> print(dtype_for_data(False, 1, 2))
float64
>>> try:
...     dtype_for_data(False, nan, '0')
... except TypeError as v:
...     print(v)
upper_bound should be an instance of any in {float, int} but is str, namely '0'.
>>> try:
...     dtype_for_data(True, 'x', 0)
... except TypeError as v:
...     print(v)
lower_bound should be an instance of any in {float, int} but is str, namely 'x'.
>>> try:
...     dtype_for_data(True, 1.0, 2.0)
... except TypeError as v:
...     print(v)
finite lower_bound of always_int should be an instance of int but is float, namely 1.0.
>>> try:
...     dtype_for_data(True, 0, 2.0)
... except TypeError as v:
...     print(v)
finite upper_bound of always_int should be an instance of int but is float, namely 2.0.
>>> try:
...     dtype_for_data(3, 0, 2)
... except TypeError as v:
...     print(v)
always_int should be an instance of bool but is int, namely 3.
moptipy.utils.nputils.fill_in_canonical_permutation(a)[source]

Fill the canonical permutation into an array.

Return type:

None

>>> import numpy
>>> arr = numpy.empty(10, int)
>>> fill_in_canonical_permutation(arr)
>>> print(arr)
[0 1 2 3 4 5 6 7 8 9]
moptipy.utils.nputils.int_range_to_dtype(min_value, max_value, force_signed=False)[source]

Convert an integer range to an appropriate numpy data type.

The returned type is as compact as possible and signed types are preferred over unsigned types. The returned numpy.dtype will allow accommodating all values in the inclusive interval min_value..max_value.

Parameters:
  • min_value (int) – the minimum value

  • max_value (int) – the maximum value

  • force_signed (bool, default: False) – enforce signed types

Return type:

dtype

Returns:

the numpy integer range

Raises:
  • TypeError – if the parameters are not integers

  • ValueError – if the range is invalid, i.e., if min_value exceeds max_value or either of them exceeds the possible range of the largest numpy integers.

>>> from moptipy.utils.nputils import int_range_to_dtype
>>> print(int_range_to_dtype(0, 127))
int8
>>> print(int_range_to_dtype(0, 128))
uint8
>>> print(int_range_to_dtype(0, 128, True))
int16
>>> print(int_range_to_dtype(0, 32767))
int16
>>> print(int_range_to_dtype(0, 32768))
uint16
>>> print(int_range_to_dtype(0, 32768, True))
int32
>>> print(int_range_to_dtype(0, (2 ** 31) - 1))
int32
>>> print(int_range_to_dtype(0, 2 ** 31))
uint32
>>> print(int_range_to_dtype(0, 2 ** 31, True))
int64
>>> print(int_range_to_dtype(0, (2 ** 63) - 1))
int64
>>> print(int_range_to_dtype(0, 2 ** 63))
uint64
>>> print(int_range_to_dtype(0, (2 ** 64) - 1))
uint64
>>> try:
...     int_range_to_dtype(0, (2 ** 64) - 1, True)
... except ValueError as e:
...     print(e)
Signed integer range cannot exceed -9223372036854775808..9223372036854775807, but 0..18446744073709551615 was specified.
>>> try:
...     int_range_to_dtype(0, (2 ** 64) + 1)
... except ValueError as e:
...     print(e)
max_value for unsigned integers must be <=18446744073709551615, but is 18446744073709551617 for min_value=0.
>>> try:
...     int_range_to_dtype(-1, (2 ** 64) - 1)
... except ValueError as e:
...     print(e)
Signed integer range cannot exceed -9223372036854775808..9223372036854775807, but -1..18446744073709551615 was specified.
>>> try:
...     int_range_to_dtype(-1.0, (2 ** 64) - 1)
... except TypeError as e:
...     print(e)
min_value should be an instance of int but is float, namely -1.0.
>>> try:
...     int_range_to_dtype(-1, 'a')
... except TypeError as e:
...     print(e)
max_value should be an instance of int but is str, namely 'a'.
moptipy.utils.nputils.is_all_finite(a)[source]

Check if an array is all finite.

Parameters:

a (ndarray) – the input array

Return type:

bool

Returns:

True if all elements in the array are finite, False otherwise

>>> import numpy as npx
>>> from moptipy.utils.nputils import is_all_finite
>>> print(is_all_finite(npx.array([1.1, 2.1, 3])))
True
>>> print(is_all_finite(npx.array([1, 2, 3])))
True
>>> print(is_all_finite(npx.array([1.1, npx.inf, 3])))
False
moptipy.utils.nputils.is_np_float(dtype)[source]

Check whether a numpy.dtype is a floating point type.

Parameters:

dtype (dtype) – the type

Return type:

bool

>>> import numpy as npx
>>> from moptipy.utils.nputils import is_np_float
>>> print(is_np_float(npx.dtype(npx.int8)))
False
>>> print(is_np_float(npx.dtype(npx.uint16)))
False
>>> print(is_np_float(npx.dtype(npx.float64)))
True
moptipy.utils.nputils.is_np_int(dtype)[source]

Check whether a numpy.dtype is an integer type.

Parameters:

dtype (dtype) – the type

Return type:

bool

>>> import numpy as npx
>>> from moptipy.utils.nputils import is_np_int
>>> print(is_np_int(npx.dtype(npx.int8)))
True
>>> print(is_np_int(npx.dtype(npx.uint16)))
True
>>> print(is_np_int(npx.dtype(npx.float64)))
False
moptipy.utils.nputils.np_ints_max(shape, dtype=dtype('int64'))[source]

Create an integer array of the given length filled with the maximum value.

Parameters:
  • shape – the requested shape

  • dtype (dtype, default: dtype('int64')) – the data type (defaults to 64bit integers)

Return type:

ndarray

Returns:

the new array

>>> import numpy as npx
>>> from moptipy.utils.nputils import np_ints_max
>>> print(np_ints_max(4, npx.dtype("uint8")))
[255 255 255 255]
moptipy.utils.nputils.np_to_py_number(number)[source]

Convert a scalar number from numpy to a corresponding Python type.

Parameters:

number (Any) – the numpy number

Return type:

int | float

Returns:

an integer or float representing the number

>>> type(np_to_py_number(1))
<class 'int'>
>>> type(np_to_py_number(1.0))
<class 'float'>
>>> type(np_to_py_number(np.int8(1)))
<class 'int'>
>>> type(np_to_py_number(np.float64(1)))
<class 'float'>
>>> try:
...    np_to_py_number(np.complex64(1))
... except TypeError as te:
...    print(te)
number should be an instance of any in {float, int, numpy.floating, numpy.integer} but is numpy.complex64.
moptipy.utils.nputils.numpy_type_to_str(dtype)[source]

Convert a numpy data type to a string.

Parameters:

dtype (dtype) – the data type

Return type:

str

Returns:

a string representation

>>> import numpy as npx
>>> numpy_type_to_str(npx.dtype(int))
'l'
>>> numpy_type_to_str(npx.dtype(float))
'd'
moptipy.utils.nputils.rand_generator(seed)[source]

Instantiate a random number generator from a seed.

Parameters:

seed (int) – the random seed

Return type:

Generator

Returns:

the random number generator

>>> type(rand_generator(1))
<class 'numpy.random._generator.Generator'>
>>> type(rand_generator(1).bit_generator)
<class 'numpy.random._pcg64.PCG64'>
>>> rand_generator(1).random() == rand_generator(1).random()
True
moptipy.utils.nputils.rand_seed_check(rand_seed)[source]

Make sure that a random seed is valid.

Parameters:

rand_seed (Any) – the random seed to check

Return type:

int

Returns:

the rand seed

Raises:
>>> rand_seed_check(1)
1
>>> rand_seed_check(0)
0
>>> try:
...     rand_seed_check(-1)
... except ValueError as ve:
...     print(ve)
rand_seed=-1 is invalid, must be in 0..18446744073709551615.
>>> rand_seed_check(18446744073709551615)
18446744073709551615
>>> try:
...     rand_seed_check(18446744073709551616)
... except ValueError as ve:
...     print(ve)
rand_seed=18446744073709551616 is invalid, must be in 0..18446744073709551615.
>>> try:
...     rand_seed_check(1.2)
... except TypeError as te:
...     print(te)
rand_seed should be an instance of int but is float, namely 1.2.
moptipy.utils.nputils.rand_seed_generate(random=Generator(PCG64) at 0x7F66BC07B3E0)[source]

Draw a (pseudo-random) random seed.

This method either uses a provided random number generator random or a default generator. It draws 8 bytes from this generator and converts them to an unsigned (64 bit) integer big-endian style.

Parameters:

random (Generator, default: Generator(PCG64) at 0x7F66BC07B3E0) – the random number generator to be used to generate the seed

Return type:

int

Returns:

the random seed

Raises:

TypeError – if random is specified but is not an instance of Generator

>>> from numpy.random import default_rng as drg
>>> rand_seed_generate(default_rng(100))
10991970318022328789
>>> rand_seed_generate(default_rng(100))
10991970318022328789
>>> rand_seed_generate(default_rng(10991970318022328789))
11139051376468819756
>>> rand_seed_generate(default_rng(10991970318022328789))
11139051376468819756
>>> rand_seed_generate(default_rng(11139051376468819756))
16592984639586750386
>>> rand_seed_generate(default_rng(11139051376468819756))
16592984639586750386
>>> rand_seed_generate(default_rng(16592984639586750386))
12064014979695949294
>>> rand_seed_generate(default_rng(16592984639586750386))
12064014979695949294
moptipy.utils.nputils.rand_seeds_from_str(string, n_seeds)[source]

Reproducibly generate n_seeds unique random seeds from a string.

This function will produce a sorted sequence of n_seeds random seeds, each of which being an unsigned 64-bit integer, from the string passed in. The same string will always yield the same sequence reproducibly. Running the function twice with different values of n_seeds will result in the two sets of random seeds, where the larger one (for the larger value of n_seeds) contains all elements of the smaller one.

This works as follows: First, we encode the string to an array of bytes using the UTF-8 encoding (string.encode(“utf8”)). Then, we compute the SHA-512 digest of this byte array (using hashlib.sha512). From this digest, we then use two chunks of 32 bytes (256 bit) to seed two PCG64 random number generators. We then alternatingly draw seeds from these two generators using rand_seed_generate() until we have n_seeds unique values.

This procedure is used in moptipy.api.experiment.run_experiment() to draw the random seeds for the algorithm runs to be performed. As string input, that method uses the string representation of the problem instance. This guarantees that all algorithms start with the same seeds on the same problems. It also guarantees that an experiment is repeatable, i.e., will use the same seeds when executed twice. Finally, it ensures that cherry-picking is impossible, as all seeds are fairly pseudo-random.

  1. Penny Pritzker and Willie E. May, editors, Secure Hash Standard (SHS), Federal Information Processing Standards Publication FIPS PUB 180-4, Gaithersburg, MD, USA: National Institute of Standards and Technology, Information Technology Laboratory, August 2015. doi: https://dx.doi.org/10.6028/NIST.FIPS.180-4 https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf

  2. Unicode Consortium, editors, The Unicode(R) Standard, Version 15.0 - Core Specification, Mountain View, CA, USA: Unicode, Inc., September 2022, ISBN:978-1-936213-32-0, https://www.unicode.org/versions/Unicode15.0.0/

  3. NumPy Community, Permuted Congruential Generator (64-bit, PCG64), in NumPy Reference, Release 1.23.0, June 2022, Austin, TX, USA: NumFOCUS, Inc., https://numpy.org/doc/1.23/numpy-ref.pdf

  4. Melissa E. O’Neill: PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation, Report HMC-CS-2014-0905, September 5, 2014, Claremont, CA, USA: Harvey Mudd College, Computer Science Department. https://www.cs.hmc.edu/tr/hmc-cs-2014-0905.pdf

Parameters:
  • string (str) – the string

  • n_seeds (int) – the number of seeds

Return type:

list[int]

Returns:

a list of random seeds

Raises:
  • TypeError – if the parameters do not follow the type contract

  • ValueError – if the parameter values are invalid

>>> rand_seeds_from_str("hello world!", 1)
[11688012229199056962]
>>> rand_seeds_from_str("hello world!", 2)
[3727742416375614079, 11688012229199056962]
>>> rand_seeds_from_str("hello world!", 3)
[3727742416375614079, 11688012229199056962, 17315292100125916507]
>>> rand_seeds_from_str("metaheuristic optimization", 1)
[12323230366215963648]
>>> rand_seeds_from_str("metaheuristic optimization", 2)
[12323230366215963648, 13673960948036381176]
>>> rand_seeds_from_str("metaheuristic optimization", 3)
[12323230366215963648, 13673960948036381176, 18426184104943646060]

moptipy.utils.number_renderer module

The numeric format definitions.

moptipy.utils.number_renderer.DEFAULT_NUMBER_RENDERER: Final[NumberRenderer] = <moptipy.utils.number_renderer.NumberRenderer object>

the default shared singleton instance of the number group format

class moptipy.utils.number_renderer.NumberRenderer(int_to_float_threshold=10000000000, get_int_renderer=<function default_get_int_renderer>, get_float_format=<function default_get_float_format>)[source]

Bases: object

A format description for a group of numbers.

With instances of this class, you can convert a sequence of numbers to a sequence of strings with uniform, pleasant formatting. The idea is that such numbers can be written, e.g., into a column of a table and that this column will then have a nice and uniform appearance. In other words, we will avoid situations like the following: “1234938845, 1e-20, 0.002, 34757773, 1e30, 0.9998837467” which looks rather odd. While the numbers may be displayed correctly, the formatting of all numbers is different. If we want to present numbers that describe related quantities, we rather want them to all have the same format. This class here can achieve this in a customizable way.

derive(int_to_float_threshold=None, get_int_renderer=None, get_float_format=None)[source]

Derive a new number group format from this one.

Parameters:
Return type:

NumberRenderer

Returns:

a new number group format that differs from the current format only in terms of the non-None parameters specified

>>> d = DEFAULT_NUMBER_RENDERER
>>> d.derive() is d
True
>>> d.int_to_float_threshold
10000000000
>>> from moptipy.utils.lang import EN
>>> EN.set_current()
>>> d.get_int_renderer()(123456789)
"123'456'789"
>>> d.get_float_format(-10, 10, 2)
'{:.2f}'
>>> d = d.derive(int_to_float_threshold=22)
>>> d is DEFAULT_NUMBER_RENDERER
False
>>> d.int_to_float_threshold
22
>>> d = d.derive(get_int_renderer=lambda: lambda x: "bla")
>>> d.get_int_renderer()(112)
'bla'
get_float_format: Final[Callable[[int | float, int | float, int, int | float, int | float], str]]

the getter for the float format to be used to represent a range of values

get_int_renderer: Final[Callable[[], Callable[[int], str]]]

the function to be used to get the renderer for all integers and integer parts of floats

int_to_float_threshold: Final[int | float]

the absolute threshold above which all integer numbers must be converted to floats to render them in the ‘E’ notation

render(source, none_str=None)[source]

Convert a sequence of numbers to text with uniform shape.

Often, we need to convert a set of numbers to strings as output for a table or another representative thext. In such a case, you want to present all numbers in the set in the same format.

Imagine you have the number vector [1E-4, 1/7, 123456789012345678]. If you simply convert this list to a string directly, what you get is [0.0001, 0.14285714285714285, 123456789012345678]. Now this looks very ugly. First, we have one very big number 123456789012345678. If the numbers stem from an experiment, then we are hardly able to obtain any number at a very extreme precision. The 18 digits in 123456789012345678 sort of suggest a precision to 18 decimals, since the number ends in specific digits (as opposed to 123450000000000000 which a reader would naturally preceive as a rounded quantity). Additionally, we have the number 0.14285714285714285, which has a very long fractional part, which, too, suggests a very high precision. Writing both mentioned numbers next to each other, this suggests as if we could present a number as high as 10**18 at a precision of 10**-17. And it also looks ugly, because both numbers are not uniformly formatted. Instead, our function here renders the number list as [‘1.00*10^-4^’, ‘1.43*10^-1^’, ‘1.23*10^17^’]. It recognizes that we should present numbers as powers of ten and then limits the precision to three digits.

This function is thus intended to produce some sort of uniform format with reasonable precision uniformly for a numerical vector, under the assumption that all numbers should be presented in the same numerical range and quantity.

Parameters:
Return type:

list[FormattedStr | None]

Returns:

a list with the text representation

>>> from moptipy.utils.lang import EN
>>> EN.set_current()
>>> ff = DEFAULT_NUMBER_RENDERER
>>> ff.render([1.75651, 212, 3234234])
['2', '212', "3'234'234"]
>>> ff.render([1.75651, 22, 34])
['1.757', '22.000', '34.000']
>>> ff.render([1.75651, 122, 34])
['1.76', '122.00', '34.00']
>>> ff.render([1.75651, 122, 3334])
['1.8', '122.0', "3'334.0"]
>>> ff.render([1.5, 212, 3234234])
['2', '212', "3'234'234"]
>>> ff.render([1.5, 2e12, 3234234])
['1.50e0', '2.00e12', '3.23e6']
>>> ff.render([233, 22139283482834, 3234234])
['2.33e2', '2.21e13', '3.23e6']
>>> ff.render([233, 22139283, 3234234])
['233', "22'139'283", "3'234'234"]
>>> from math import nan, inf
>>> ff.render([22139283, inf, -inf, nan, None])
["22'139'283", 'inf', '-inf', 'nan', None]
>>> ff.render([1E-4, 1/7, 123456789012345678])
['1.00e-4', '1.43e-1', '1.23e17']
>>> ff.render([0, 0.02, 0.1, 1e-3])
['0.000', '0.020', '0.100', '0.001']
>>> ff.render([-0.2, 1e-6, 0.9])
['-2.000e-1', '1.000e-6', '9.000e-1']
moptipy.utils.number_renderer.default_get_float_format(min_finite=0, max_finite=0, max_frac_len=2, min_non_zero_abs=inf, int_to_float_threshold=10000000000)[source]

Get the default float format for numbers in the given range.

Parameters:
  • min_finite (int | float, default: 0) – the minimum finite value that may need to be formatted

  • max_finite (int | float, default: 0) – the maximum finite value that may need to be formatted

  • max_frac_len (int, default: 2) – the length of the longest fractional part of any number encountered that can be converted to a string not in the “E” notation

  • min_non_zero_abs (int | float, default: inf) – the minimum non-zero absolute value; will be inf if all absolute values are zero

  • int_to_float_threshold (int | float, default: 10000000000) – the threshold above which all integers are converted to floating point numbers with the ‘E’ notation

Return type:

str

>>> default_get_float_format(0, 0, 0)
'{:.0f}'
>>> default_get_float_format(0, 1e5, 10)
'{:.0f}'
>>> default_get_float_format(-1e7, 1e2, 10)
'{:.0f}'
>>> default_get_float_format(0, 0, 1)
'{:.1f}'
>>> default_get_float_format(0, 1e3, 11)
'{:.1f}'
>>> default_get_float_format(-1e3, 1e2, 11)
'{:.1f}'
>>> default_get_float_format(0, 0, 2)
'{:.2f}'
>>> default_get_float_format(0, 0, 3)
'{:.3f}'
>>> default_get_float_format(0, 0, 4)
'{:.3f}'
>>> default_get_float_format(0, 1e11, 4)
'{:.2e}'
>>> default_get_float_format(-1, 1, 4, 1e-3)
'{:.3f}'
moptipy.utils.number_renderer.default_get_int_renderer()[source]

Get the default integer renderer.

Return type:

Callable[[int], str]

Returns:

the default integer renderer, which uses the integer rendering of the currently active language setting.

>>> from moptipy.utils.lang import EN, ZH
>>> EN.set_current()
>>> f = default_get_int_renderer()
>>> f(1_000_000)
"1'000'000"
>>> ZH.set_current()
>>> f = default_get_int_renderer()
>>> f(1_000_000)
"100'0000"

moptipy.utils.plot_defaults module

Default styles for plots.

moptipy.utils.plot_defaults.COLOR_BLACK: Final[tuple[float, float, float]] = (0.0, 0.0, 0.0)

The internal color black.

moptipy.utils.plot_defaults.COLOR_WHITE: Final[tuple[float, float, float]] = (1.0, 1.0, 1.0)

The internal color white.

moptipy.utils.plot_defaults.GRID_COLOR: Final[tuple[float, float, float]] = (0.6363636363636364, 0.6363636363636364, 0.6363636363636364)

The default grid color

moptipy.utils.plot_defaults.LINE_DASH_SOLID: Final[str] = 'solid'

The solid line dash

moptipy.utils.plot_defaults.create_line_style(**kwargs)[source]

Obtain the basic style for lines in diagrams.

Parameters:

kwargs – any additional overrides

Return type:

dict[str, object]

Returns:

a dictionary with the style elements

moptipy.utils.plot_defaults.distinct_colors(n)[source]

Obtain a set of n distinct colors.

Parameters:

n (int) – the number of colors required

Return type:

tuple[tuple[float, float, float], ...]

Returns:

a tuple of colors

moptipy.utils.plot_defaults.distinct_line_dashes(n)[source]

Create a sequence of distinct line dashes.

Parameters:

n (int) – the number of styles

Return type:

tuple[str | tuple[float, tuple[float, ...]], ...]

Returns:

the styles

moptipy.utils.plot_defaults.distinct_markers(n)[source]

Create a sequence of distinct markers.

Parameters:

n (int) – the number of markers

Return type:

tuple[str, ...]

Returns:

the markers

moptipy.utils.plot_defaults.importance_to_alpha(importance)[source]

Transform an importance value to an alpha value.

Basically, an importance of 0 indicates a normal line in a normal plot that does not need to be emphasized. A positive importance means that the line should be emphasized. A negative importance means that the line should be de-emphasized.

Parameters:

importance (int) – a value between -9 and 9

Return type:

float

Returns:

the alpha

moptipy.utils.plot_defaults.importance_to_font_size(importance)[source]

Transform an importance value to a font size.

Parameters:

importance (float) – the importance value

Return type:

float

Returns:

the font size

moptipy.utils.plot_defaults.importance_to_line_width(importance)[source]

Transform an importance value to a line width.

Basically, an importance of 0 indicates a normal line in a normal plot that does not need to be emphasized. A positive importance means that the line should be emphasized. A negative importance means that the line should be de-emphasized.

Parameters:

importance (int) – a value between -9 and 9

Return type:

float

Returns:

the line width

moptipy.utils.plot_defaults.rgb_to_gray(r, g, b)[source]

Convert RGB values to gray scale.

Parameters:
  • r (float) – the red value

  • g (float) – the green value

  • b (float) – the blue value

Return type:

float

Returns:

the gray value

moptipy.utils.plot_defaults.str_to_palette(palette)[source]

Obtain a palette from a string.

Parameters:

palette (str) – the string with all the color data.

Return type:

tuple[tuple[float, float, float], ...]

Returns:

the palette

moptipy.utils.plot_defaults.text_color_for_background(background)[source]

Get a reasonable text color for a given background color.

Parameters:

background (tuple[float, float, float]) – the background color

Return type:

tuple[float, float, float]

Returns:

the text color

moptipy.utils.plot_utils module

Utilities for creating and storing figures.

moptipy.utils.plot_utils.cm_to_inch(cm)[source]

Convert cm to inch.

Parameters:

cm (int | float) – the cm value

Return type:

float

Returns:

the value in inch

moptipy.utils.plot_utils.create_figure(width=8.6, height=None, dpi=384.0, **kwargs)[source]

Create a matplotlib figure.

Parameters:
  • width (float | int | None, default: 8.6) – the optional width

  • height (float | int | None, default: None) – the optional height

  • dpi (float | int | None, default: 384.0) – the dpi value

  • kwargs – a set of optional arguments

Return type:

Figure

Returns:

the figure option

moptipy.utils.plot_utils.create_figure_with_subplots(items, max_items_per_plot=3, max_rows=3, max_cols=1, min_rows=1, min_cols=1, default_width_per_col=8.6, max_width=8.6, default_height_per_row=5.315092303249095, max_height=9, dpi=384.0, plot_config=None, **kwargs)[source]

Divide a figure into nrows*ncols sub-plots.

Parameters:
  • items (int) – the number of items to divide

  • max_items_per_plot (int, default: 3) – the maximum number of items per plot

  • max_rows (int, default: 3) – the maximum number of rows

  • max_cols (int, default: 1) – the maximum number of columns

  • min_rows (int, default: 1) – the minimum number of rows

  • min_cols (int, default: 1) – the minimum number of cols

  • default_width_per_col (float | int | None, default: 8.6) – the optional default width of a column

  • default_height_per_row (float | int | None, default: 5.315092303249095) – the optional default height per row

  • max_height (float | int | None, default: 9) – the maximum height

  • max_width (float | int | None, default: 8.6) – the maximum width

  • dpi (float | int | None, default: 384.0) – the dpi value

  • kwargs – a set of optional arguments

  • plot_config (dict[str, Any] | None, default: None) – the configuration to be applied to all sub-plots

Return type:

tuple[Figure, tuple[tuple[Axes | Figure, int, int, int, int, int], ...]]

Returns:

a tuple with the figure, followed by a series of tuples with each sub-figure, the index of the first item assigned to it, the index of the last item assigned to it + 1, their row, their column, and their overall index

moptipy.utils.plot_utils.get_axes(figure)[source]

Obtain the axes from a figure or axes object.

Parameters:

figure (Axes | Figure) – the figure

Return type:

Axes

Returns:

the Axes

moptipy.utils.plot_utils.get_label_colors(handles, color_map=None, default_color=(0.0, 0.0, 0.0))[source]

Get a list with label colors from a set of artists.

Parameters:
Return type:

list[tuple[float, ...] | str]

Returns:

a list of label colors

moptipy.utils.plot_utils.get_renderer(figure)[source]

Get a renderer that can be used for determining figure element sizes.

Parameters:

figure (Axes | Figure | SubFigure) – the figure element

Return type:

RendererBase

Returns:

the renderer

moptipy.utils.plot_utils.label_axes(axes, x_label=None, x_label_inside=True, x_label_location=0.5, y_label=None, y_label_inside=True, y_label_location=1, font_size=8.0, z_order=None)[source]

Put labels on a figure.

Parameters:
  • axes (Axes) – the axes to add the label to

  • x_label (str | None, default: None) – a callable returning the label for the x-axis, a label string, or None if no label should be put

  • x_label_inside (bool, default: True) – put the x-axis label inside the plot (so that it does not consume additional vertical space)

  • x_label_location (float, default: 0.5) – the location of the x-axis label if it is placed inside the plot area

  • y_label (str | None, default: None) – a callable returning the label for the y-axis, a label string, or None if no label should be put

  • y_label_inside (bool, default: True) – put the xyaxis label inside the plot (so that it does not consume additional horizontal space)nal vertical space)

  • y_label_location (float, default: 1) – the location of the y-axis label if it is placed inside the plot area

  • font_size (float, default: 8.0) – the font size to use

  • z_order (float | None, default: None) – an optional z-order value

Return type:

None

moptipy.utils.plot_utils.label_box(axes, text, x=None, y=None, font_size=8.0, may_rotate_text=False, z_order=None, font=<function <lambda>>)[source]

Put a label text box near an axis.

Parameters:
  • axes (Axes) – the axes to add the label to

  • text (str) – the text to place

  • x (float | None, default: None) – the location along the x-axis: 0 means left, 0.5 means centered, 1 means right

  • y (float | None, default: None) – the location along the x-axis: 0 means bottom, 0.5 means centered, 1 means top

  • font_size (float, default: 8.0) – the font size

  • may_rotate_text (bool, default: False) – should we rotate the text by 90° if that makes sense (True) or always keep it horizontally (False)

  • z_order (float | None, default: None) – an optional z-order value

  • font (Union[None, str, Callable], default: <function <lambda> at 0x7f668579b9c0>) – the font to use

Return type:

None

moptipy.utils.plot_utils.save_figure(fig, file_name='figure', dir_name='.', formats='svg')[source]

Store the given figure in files of the given formats and dispose it.

Parameters:
  • fig (Figure) – the figure to save

  • file_name (str, default: 'figure') – the basic file name

  • dir_name (str, default: '.') – the directory name

  • formats (Union[str, Iterable[str]], default: 'svg') – the file format(s)

Return type:

list[Path]

Returns:

a list of files

moptipy.utils.strings module

Routines for handling strings.

moptipy.utils.strings.DECIMAL_DOT_REPLACEMENT: Final[str] = 'd'

the replacement for “.” in a file name

moptipy.utils.strings.MINUS_REPLACEMENT: Final[str] = 'm'

the replacement for “-” in a file name

moptipy.utils.strings.PART_SEPARATOR: Final[str] = '_'

the separator of different filename parts

moptipy.utils.strings.PLUS_REPLACEMENT: Final[str] = 'p'

the replacement for “+” in a file name

moptipy.utils.strings.beautify_float_str(s)[source]

Beautify the string representation of a float.

This function beautifies the string representation of a float by using unicode superscripts for exponents.

Parameters:

s (str | float) – either a float or the string representation of a float

Return type:

str

Returns:

the beautified string representation

>>> beautify_float_str('0.0')
'0.0'
>>> beautify_float_str('1e12')
'1×10¹²'
>>> beautify_float_str('1e-3')
'1×10⁻³'
>>> beautify_float_str('inf')
'∞'
>>> beautify_float_str('-inf')
'-∞'
>>> beautify_float_str('nan')
'∅'
moptipy.utils.strings.name_str_to_num(s)[source]

Convert a string from a name to a number.

This function is the inverse of num_to_str_for_name().

Parameters:

s (str) – the string from the name

Return type:

int | float

Returns:

an integer or float, depending on the number represented by s

>>> name_str_to_num(num_to_str_for_name(1.1))
1.1
>>> name_str_to_num(num_to_str_for_name(1))
1
>>> name_str_to_num(num_to_str_for_name(-5e3))
-5000
>>> name_str_to_num(num_to_str_for_name(-6e-3))
-0.006
>>> name_str_to_num(num_to_str_for_name(100.0))
100
>>> name_str_to_num(num_to_str_for_name(-1e-4))
-0.0001
moptipy.utils.strings.num_to_str_for_name(x)[source]

Convert a float to a string for use in a component name.

This function can be inverted by applying name_str_to_num().

Parameters:

x (int | float) – the float

Return type:

str

Returns:

the string

>>> num_to_str_for_name(1.3)
'1d3'
>>> num_to_str_for_name(1.0)
'1'
>>> num_to_str_for_name(-7)
'm7'
>>> num_to_str_for_name(-6.32)
'm6d32'
>>> num_to_str_for_name(-1e-5)
'm1em5'
moptipy.utils.strings.sanitize_name(name)[source]

Sanitize a name in such a way that it can be used as path component.

>>> sanitize_name(" hello world ")
'hello_world'
>>> sanitize_name(" 56.6-455 ")
'56d6m455'
>>> sanitize_name(" _ i _ am _ funny   --6 _ ")
'i_am_funny_m6'
Parameters:

name (str) – the name that should be sanitized

Return type:

str

Returns:

the sanitized name

Raises:
  • ValueError – if the name is invalid or empty

  • TypeError – if the name is None or not a string

moptipy.utils.strings.sanitize_names(names)[source]

Sanitize a set of names.

>>> sanitize_names(["", " sdf ", "", "5-3"])
'sdf_5m3'
>>> sanitize_names([" a ", " b", " c", "", "6", ""])
'a_b_c_6'
Parameters:

names (Iterable[str]) – the list of names.

Return type:

str

Returns:

the sanitized name

moptipy.utils.sys_info module

A tool for writing a section with system information into log files.

moptipy.utils.sys_info.add_dependency(dependency, ignore_if_make_build=False)[source]

Add a dependency so that its version can be stored in log files.

Warning: You must add all dependencies before the first log file is written. As soon as the log_sys_info() is invoked for the first time, adding new dependencies will cause an error. And writing a log file via the experiment API or to a file specified in the execution API will invoke this function.

Parameters:
  • dependency (str) – the basic name of the library, exactly as you would import it in a Python module. For example, to include the version of numpy in the log files, you would do add_dependency(“numpy”) (of course, the version of numpy is already automatically included anyway).

  • ignore_if_make_build (bool, default: False) – should this dependency be ignored if this method is invoked during a make build? This makes sense if the dependency itself is a package which is uninstalled and then re-installed during a make build process. In such a situation, the dependency version may be unavailable and cause an exception.

Raises:
  • TypeError – if dependency is not a string

  • ValueError – if dependency is an invalid string or the log information has already been accessed before and modifying it now is not permissible.

Return type:

None

moptipy.utils.sys_info.get_sys_info()[source]

Get the system information as string.

Return type:

str

Returns:

the system information as string

>>> raw_infos = get_sys_info()
>>> raw_infos is get_sys_info()  # caching!
True
>>> for k in raw_infos.split("\n"):
...     print(k[:k.find(": ")])
session.start
session.node
session.processId
session.cpuAffinity
session.commandLine
session.workingDirectory
session.ipAddress
version.cmaes
version.contourpy
version.cycler
version.fonttools
version.intelcmplrlibrt
version.joblib
version.kiwisolver
version.llvmlite
version.matplotlib
version.moptipy
version.numba
version.numpy
version.packaging
version.pdfo
version.pillow
version.psutil
version.pycommons
version.pyparsing
version.pythondateutil
version.scikitlearn
version.scipy
version.setuptools
version.six
version.threadpoolctl
hardware.machine
hardware.nPhysicalCpus
hardware.nLogicalCpus
hardware.cpuMhz
hardware.byteOrder
hardware.cpu
hardware.memSize
python.version
python.implementation
os.name
os.release
os.version
moptipy.utils.sys_info.log_sys_info(logger)[source]

Write the system information section to a logger.

The concept of this method is that we only construct the whole system configuration exactly once in memory and then always directly flush it as a string to the logger. This is much more efficient than querying it every single time.

Parameters:

logger (Logger) – the logger

Return type:

None

>>> from moptipy.utils.logger import InMemoryLogger
>>> with InMemoryLogger() as l:
...     log_sys_info(l)
...     log = l.get_log()
>>> print(log[0])
BEGIN_SYS_INFO
>>> print(log[-1])
END_SYS_INFO
>>> log[1:-1] == get_sys_info().split('\n')
True
moptipy.utils.sys_info.update_sys_info_cpu_affinity()[source]

Update the CPU affinity of the system information.

Return type:

None

moptipy.utils.table module

Classes for printing tables in a text format.

class moptipy.utils.table.Row(owner, mode)[source]

Bases: AbstractContextManager

A row class.

cell(text=None)[source]

Render the text of a cell.

As parameter text, you can provide either a string or a sequence of strings. You can also provide an instance of moptipy.utils.formatted_string.FormattedStr or a sequence thereof. This allows you to render formatted text in a natural fashion.

Parameters:

text (Union[str, Iterable[str], None], default: None) – the text to write

Return type:

None

class moptipy.utils.table.Rows(owner, mode)[source]

Bases: AbstractContextManager

A set of table rows.

cols(cols)[source]

Print cells and rows column-by-column.

Parameters:

cols (list[list[str | None]]) – an array which contains one list per column of the table.

Return type:

None

full_row(cells)[source]

Print a complete row with a single call.

Parameters:

cells (Iterable[str | None]) – the iterable of strings for the cells.

Return type:

None

row()[source]

Create a row.

Return type:

Row

Returns:

the new row

class moptipy.utils.table.Section(owner)[source]

Bases: Rows

A table section is a group of rows, potentially with a header.

header()[source]

Print the section header.

Return type:

Rows

Returns:

the header row

class moptipy.utils.table.Table(stream, cols, driver)[source]

Bases: AbstractContextManager

The table context.

This class provides a simple and hierarchically structured way to write tables in different formats. It only supports the most rudimentary formatting and nothing fancy (such as references, etc.). However, it may be totally enough to quickly produce tables with results of experiments.

Every table must have a table header (see header()). Every table then consists of a sequence of one or multiple sections (see section() and Section). Each table section itself may or may not have a header (see Section.header()) and must have at least one row (see Rows.row() and Row). Each row must have the exact right number of cells (see Row.cell()).

columns: Final[str]

the internal column definition

header()[source]

Construct the header of the table.

Return type:

Rows

Returns:

a new managed header row

section()[source]

Create a new section of rows.

Return type:

Section

Returns:

a new managed row section

moptipy.utils.text_format module

Infrastructure to created structured text, like Markdown and LaTeX.

moptipy.utils.text_format.MODE_NORMAL: Final[int] = 0

indicates a normal row or cell

moptipy.utils.text_format.MODE_SECTION_HEADER: Final[int] = 2

indicates a row or cell in the section header

moptipy.utils.text_format.MODE_TABLE_HEADER: Final[int] = 1

indicates a row or cell in the table header

class moptipy.utils.text_format.TextFormatDriver[source]

Bases: object

A base class for text format drivers.

Table drivers allow us to render the structured text. It used, for example, by instances of Table to write tabular data to a text format stream.

begin_table_body(stream, cols)[source]

Write the beginning of the table body.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

Return type:

None

begin_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

Begin a header cell, section header cell, or normal cell.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definitions

  • section_index (int) – the index of the current section, -1 if this is a table header cell

  • row_index (int) – the row index in the section or header

  • col_index (int) – the column index, 0 for the first column

  • cell_mode (int) – the mode of the cell, will be one of MODE_NORMAL, MODE_TABLE_HEADER, or MODE_SECTION_HEADER

Return type:

None

begin_table_header(stream, cols)[source]

Begin the header row of the table.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

Return type:

None

begin_table_row(stream, cols, section_index, row_index, row_mode)[source]

Begin a table header row, section row, or normal row in a section.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the current section, -1 if we are in the table header

  • row_index (int) – the row index in the section or header

  • row_mode (int) – the mode of the row, will be one of MODE_NORMAL, MODE_TABLE_HEADER, or MODE_SECTION_HEADER

Return type:

None

begin_table_section(stream, cols, section_index)[source]

Begin a new section of the table.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the section, 0 for the first section

Return type:

None

begin_table_section_header(stream, cols, section_index)[source]

Begin the header row of a section.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the section, 0 for the first section

Return type:

None

end_table_body(stream, cols)[source]

Write the ending of the table body.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

Return type:

None

end_table_cell(stream, cols, section_index, row_index, col_index, cell_mode)[source]

End a header cell, section header cell, or normal cell.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definitions

  • section_index (int) – the index of the current section, -1 if this is a table header cell

  • row_index (int) – the row index in the section or header

  • col_index (int) – the column index, 0 for the first column

  • cell_mode (int) – the mode of the cell, will be one of MODE_NORMAL, MODE_TABLE_HEADER, or MODE_SECTION_HEADER

Return type:

None

end_table_header(stream, cols)[source]

End the header row of the table.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

Return type:

None

end_table_row(stream, cols, section_index, row_index)[source]

End a table header row, section row, or normal row in a section.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the current section

  • row_index (int) – the row index in the section or header

Return type:

None

end_table_section(stream, cols, section_index, n_rows)[source]

End a section of the table.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the section, 0 for the first section

  • n_rows (int) – the number of rows that were written in the section

Return type:

None

end_table_section_header(stream, cols, section_index)[source]

End the header row of a section.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • cols (str) – the column definition

  • section_index (int) – the index of the section, 0 for the first section

Return type:

None

filename(file_name='file', dir_name='.', use_lang=True)[source]

Get the right filename for this text driver.

Parameters:
  • file_name (str, default: 'file') – the base file name

  • dir_name (str, default: '.') – the base directory

  • use_lang (bool, default: True) – should we use the language to define the filename?

Return type:

Path

Returns:

the path to the file to generate

text(stream, text, bold, italic, code, mode)[source]

Write a chunk of text.

Parameters:
  • stream (TextIOBase) – the stream to write to

  • text (str) – the text to write

  • bold (bool) – is the text in bold face?

  • italic (bool) – is the text in italic face?

  • code (bool) – is the text in code face?

  • mode (int) – the mode of a formatted text piece, see the attribute mode of FormattedStr

Return type:

None