helpers Module

Overview

The helpers module contains helper functions that are used by functions throughout the Stockpyl package.

API Reference

min_of_dict(d)[source]

Determine min value of a dict and return min and argmin (key).

Values must be numeric.

Parameters

d (dict) – The dict.

Returns

  • min_value (float) – Minimum value in dict.

  • min_key (any) – Key that attains minimum value.

Raises

TypeError – If dict contains a non-numeric value.

dict_match(d1, d2, require_presence=False, rel_tol=1e-09, abs_tol=0.0)[source]

Check whether two dicts have equal keys and values.

A missing key is treated as 0 if the key is present in the other dict, unless require_presence is True, in which case the dict must have the key to count as a match.

Parameters
  • d1 (node) – First dict for comparison.

  • d2 (node) – Second dict for comparison.

  • require_presence (bool, optional) – Set to True to require dicts to have the same keys, or False (default) to treat missing keys as 0s.

  • rel_tol (float) – Relative tolerance.

  • abs_tol (float) – Absolute tolerance.

is_iterable(x)[source]

Determine whether x is an iterable or a singleton.

Parameters

x (any) – Object to test for iterable vs. singleton.

Returns

True if x is iterable, False if it is a singleton.

Return type

bool

is_list(x)[source]

Determine whether x is a list.

Parameters

x (any) – Object to test for list-ness.

Returns

True if x is a list, False otherwise.

Return type

bool

is_integer(x)[source]

Determine whether x is an integer. Return False if x is not a float, or is a non-integer float, or is an int.

Parameters

x (float) – Number to check for integrality.

Returns

True if x is an integer, False otherwise.

Return type

bool

is_discrete_distribution(distribution)[source]

Check whether the given distribution object is discrete.

Works both for rv_frozen objects and for custom distributions (i.e., subclasses of rv_continuous and rv_discrete).

See https://stackoverflow.com/a/61530461/3453768.

Parameters

distribution (rv_frozen, rv_continuous, or rv_discrete) – The distribution object to check.

Returns

True if the distribution is discrete, False otherwise.

Return type

bool

Note

Not reliable if distribution is not an rv_frozen, rv_discrete, or rv_continuous object.

is_continuous_distribution(distribution)[source]

Check whether the given distribution object is continuous.

Works both for rv_frozen objects and for custom distributions (i.e., subclasses of rv_continuous and rv_discrete).

See https://stackoverflow.com/a/61530461/3453768.

Parameters

distribution (rv_frozen, rv_continuous, or rv_discrete) – The distribution object to check.

Returns

True if the distribution is continuous, False otherwise.

Return type

bool

Note

Not reliable if distribution is not an rv_frozen, rv_discrete, or rv_continuous object.

find_nearest(array, values, sorted=False, index={})[source]

Determine entries in array that are closest to each of the entries in values and return their indices. Neither array needs to be sorted, but if array is sorted and sorted is set to True, execution will be faster. In dictionary index a map to desired indices can be provided, in which case execution will be even faster for specified values. array and values need not be the same length.

Parameters
  • array (ndarray) – The array to search for values in.

  • values (ndarray) – The array whose values should be searched for in the other array.

  • sorted (bool, optional) – If True, treats array as sorted, which will make the function execute faster.

  • index (dict, optional) – A dictionary from values to indices, which will make the function execute even faster for given values.

Returns

ind – Array of indices.

Return type

ndarray

check_iterable_sizes(iterable_list)[source]

Check whether iterable_list is a list in which every item is an iterable of the same size or a singleton.

Examples:

>>> check_iterable_sizes([[5, 3, 1], ('a', 'b', 'c'), 7])
True
>>> check_iterable_sizes([[5, 3, 1], ('a', 'b'), 7])
False
Parameters

iterable_list (list) – List to check.

Returns

True if iterable_list is a list in which every item is an iterable of the same size or a singleton, False otherwise.

Return type

bool

ensure_list_for_time_periods(x, num_periods, var_name=None)[source]

Ensure that x is a list suitable for time-period indexing; if not, create such a list and return it.

“Suitable for time-period indexing” means that it has length num_periods + 1, and the 0th element is ignored.

  • If x is a singleton, return a list consisting of num_periods copies of x plus a 0 in the 0th element.

  • If x is a list of length num_periods``+1, return ``x.

  • If x is a list of length num_periods, shift elements to the right by 1 slot, fill the 0th element with 0, and return new list.

  • Otherwise, raise a ValueError.

Examples:

>>> ensure_list_for_time_periods(5, 3)
[0, 5, 5, 5]
>>> ensure_list_for_time_periods([0, 5, 2, 1], 4)
[0, 0, 5, 2, 1]
>>> ensure_list_for_time_periods([5, 2, 1, 3, 2], 4)
[5, 2, 1, 3, 2]
>>> ensure_list_for_time_periods([0, 5, 2, 1, 5], 3)
Traceback (most recent call last):
        ...
ValueError: x must be a singleton or a list of length num_periods or num_periods+1
Parameters
  • x (float or list) – Object to time-period-ify.

  • num_periods (int) – Number of time periods.

  • var_name (str, optional) – Variable name to use in generating error messages, if desired. Useful for offloading the type-checking to this function rather than doing it in the calling function.

Returns

x_new – Time-period-ified list.

Return type

list

Raises

ValueError – If x is not a singleton or a list of length num_periods or num_periods + 1.

ensure_list_for_nodes(x, num_nodes, default=None)[source]

Ensure that x is a list suitable for node indexing; if not, create such a list and return it.

“Suitable for node indexing” means that it has length num_nodes.

  • If x is a singleton, return a list consisting of num_nodes copies of x.

  • If x is a list of length num_nodes, return x.

  • If x is None and default is provided, return a list consisting of num_nodes copies of default.

  • If x is None and default is not provided, a list consisting of num_nodes copies of None.

  • Otherwise, raise a ValueError.

Examples:

>>> ensure_list_for_nodes(5, 3)
[5, 5, 5]
>>> ensure_list_for_nodes([0, 5, 2, 1], 4)
[0, 5, 2, 1]
>>> ensure_list_for_nodes([0, 5, 2, 1], 3)
Traceback (most recent call last):
        ...
ValueError: x must be a singleton, a list of length num_nodes, or None
Parameters
  • x (float or list) – Object to node-ify.

  • num_nodes (int) – Number of nodes.

  • default (float, optional) – Value to use if x is None.

Returns

x_new – Node-ified list.

Return type

list

Raises

ValueError – If x is not a singleton, a list of length num_nodes, or None.

build_node_data_dict(attribute_dict, node_order_in_lists, default_values={})[source]

Build a dict of dicts containing data for all nodes and for one or more attributes. The dict returned, data_dict, is keyed by the node indices. data_dict[n] is a dict of data for the node with index n, and data_dict[n][a] is the value of attribute a, where a is in a key in attribute_dict.

build_node_data_dict() is similar calling ensure_dict_for_nodes() for multiple attributes simultaneously.

For each attribute a in attribute_dict keys, attribute_dict[a] may take one of several forms. For a given node index n and attribute a:

  • If attribute_dict[a] is a dict, then data_dict[n][a] is set to attribute_dict[a][n]. If n is not a key in attribute_dict[a] then data_dict[n][a] is set to default_values[a] if it exists and to None otherwise.

  • If attribute_dict[a] is a singleton, then data_dict[n][a] is set to attribute_dict[a].

  • If attribute_dict[a] is a list with the same length as node_order_in_lists, then the values in the list are assumed to correspond to the node indices in the order they are specified in node_order_in_lists. That is, attribute_dict[a][k] is placed in data_dict[node_order_in_lists[k]][a], for k in range(len(node_order_in_lists)).

  • If attribute_dict[a] is None and default_values[a] is provided, data_dict[n][a] is set to default_values[a]. (This is useful for setting default values for attributes that are passed without knowing whether data is provided for them.)

  • If attribute_dict[a] is None and default_values[a] is not provided, data_dict[n][a] is set to None.

  • If attribute_dict[a] is a list that does not have the same length as node_order_in_lists, a ValueError is raised.

(Exception: The demand_list and probabilities attributes of DemandSource are lists, and are treated as singletons for the purposes of the rules above, unless they contain other lists, in which case they are treated as normal. For example, attribute_dict['demand_list'] = [0, 1, 2, 3] will set the demand_list to [0, 1, 2, 3] for all nodes. But demand_list=[[0, 1, 2, 3], [0, 1, 2, 3], None, None] will set the demand_list to [0, 1, 2, 3] for nodes 0 and 1 but to None for nodes 2 and 3.)

Parameters
  • attribute_dict (dict) – Dict whose keys are strings (representing node attributes) and whose values are dicts, lists, and/or singletons specifying the values of the attributes for the nodes.

  • node_order_in_lists (list) – List of node indices in the order in which the nodes are listed in any attributes that are lists. (node_order_in_lists[k] is the index of the k th node.)

  • default_values (dict) – Dict whose keys are strings (in attribute_dict.keys() ) and whose values are the default values to use if the attribute is not provided.

Returns

data_dict – The data dict.

Return type

dict

Raises

ValueError – If attribute_dict[a] is a list whose length does not equal that of node_order_in_lists.

Example:

>>> attribute_dict = {}
>>> attribute_dict['local_holding_cost'] = 1
>>> attribute_dict['stockout_cost'] = [10, 8, 0]
>>> attribute_dict['demand_mean'] = {1: 0, 3: 50}
>>> attribute_dict['lead_time'] = None
>>> attribute_dict['processing_time'] = None
>>> node_order_in_lists = [3, 2, 1]
>>> default_values = {'lead_time': 0, 'demand_mean': 99}
>>> data_dict = build_node_data_dict(attribute_dict, node_order_in_lists, default_values)
>>> data_dict[1]
{'local_holding_cost': 1, 'stockout_cost': 0, 'demand_mean': 0, 'lead_time': 0, 'processing_time': None}
>>> data_dict[2]
{'local_holding_cost': 1, 'stockout_cost': 8, 'demand_mean': 99, 'lead_time': 0, 'processing_time': None}
>>> data_dict[3]
{'local_holding_cost': 1, 'stockout_cost': 10, 'demand_mean': 50, 'lead_time': 0, 'processing_time': None}
ensure_dict_for_nodes(x, node_indices, default=None)[source]

Ensure that x is a dict with node indices as keys; if not, create such a dict and return it.

  • If x is a dict, return x.

  • If x is a singleton, return a dict with keys equal to node_indices and values all equal to x.

  • If x is a list with the same length as node_indices, return a dict with keys equal to node_indices and values equal to x.

  • If x is None and default is provided, return a dict with keys equal to node_indices and values all equal to default.

  • If x is None and default is not provided, return a dict with keys equal to node_indices and values all equal to None.

  • Otherwise, raise a ValueError.

Parameters
  • x (dict, float, or list) – Object to node-ify.

  • node_indices (list) – List of node indices. (node_indices[k] is the index of the k th node.)

  • default (float, optional) – Value to use if x is None.

Returns

x_new – Node-ified dict.

Return type

dict

Raises

ValueError – If x is not a dict, a singleton, a list with the same length as node_indices, or None.

Examples:

>>> ensure_dict_for_nodes(5, [0, 1, 2])
{0: 5, 1: 5, 2: 5}
>>> ensure_dict_for_nodes([0, 5, 2], [0, 1, 2])
{0: 0, 1: 5, 2: 2}
>>> ensure_list_for_nodes([0, 5, 2, 1], [0, 1, 2])
Traceback (most recent call last):
        ...
ValueError: x must be a singleton, dict, list with the same length as node_indices, or None
sort_dict_by_keys(d, ascending=True, return_values=True)[source]

Sort dict by keys and return sorted list of values or keys, depending on the value of return_values.

Special handling is included to handle keys that might be None. (None is assumed to come before any other element when sorting in ascending order.)

Parameters
  • d (dict) – The dict to sort.

  • ascending (bool, optional) – Sort order.

  • return_values (bool, optional) – Set to True to return a list of the dict’s values, False to return its keys.

Returns

return_list – List of values or keys of d, sorted in order of keys of d.

Return type

list

change_dict_key(dict_to_change, old_key, new_key)[source]

Change old_key to new_key in dict_to_change (in place). New key/value pair will appear at end of dict.

Parameters
  • dict_to_change (dict) – The dict.

  • old_key – The key to be changed.

  • new_key – The key to change to.

Raises

KeyError – If dict_to_change[old_key] is undefined.

convolve_many(arrays)[source]

Convolve a list of 1-dimensional float arrays together, using fast Fourier transforms (FFTs). The arrays need not have the same length, but each array should have length at least 1.

If the arrays represent pmfs of discrete random variables \(X_1,\ldots,X_n\), then the output represents the pmf of \(X_1+\cdots+X_n\). Assuming the possible values of all of the random variables are equally spaced with spacing \(s\), the possible values of \(X_1+\cdots+X_n\) corresponding to the output are \(\sum_i\min X_i,\ldots,\sum_i \max X_i\), with spacing \(s\).

Code is adapted from https://stackoverflow.com/a/29236193/3453768.

Parameters

arrays (list of 1-dimensional float arrays) – The arrays to convolve.

Returns

convolution – Array of elements in the convolution.

Return type

array

Example: Let \(X_1 = \{0, 1, 2\}\) with probabilities \([0.6, 0.3, 0.1]\), \(X_2 = \{0, 1, 2\}\) with probabilities \([0.5, 0.4, 0.1]\), \(X_3 = \{0, 1\}\) with probabilities \([0.3, 0.7]\), and \(X_4 = 0\) with probability \(1\).

>>> convolve_many([[0.6, 0.3, 0.1], [0.5, 0.4, 0.1], [0.3, 0.7], [1.0]])
array([0.09 , 0.327, 0.342, 0.182, 0.052, 0.007])

In other words, \(X_1+\cdots+X_4 = \{0, 1, \ldots, 5\}\) with probabilities \([0.09 , 0.327, 0.342, 0.182, 0.052, 0.007]\).

irwin_hall_cdf(x, n)[source]

Return cdf of Irwin-Hall distribution, i.e., distribution of sum of n \(U[0,1]\) random variables.

See https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution.

Parameters
  • x (float) – Argument of cdf function.

  • n (int) – Number of \(U[0,1]\) random variables in the sum.

Returns

F – The cdf of x.

Return type

float

sum_of_continuous_uniforms_distribution(n, lo=0, hi=1)[source]

Return distribution of sum of n identical continuous uniform random variables as an rv_continuous object.

If lo = 0 and hi = 1, this distribution is the Irwin-Hall distribution.

Parameters
  • n (int) – Number of uniform random variables in the sum.

  • lo (float, optional) – Lower bound of uniform distribution. Default = 0.

  • hi (float, optional) – Upper bound of uniform distribution. Default = 1.

Returns

distribution – The rv_continuous object.

Return type

rv_continuous

Raises

ValueError – If n is not an integer.

sum_of_discrete_uniforms_pmf(n, lo, hi)[source]

Calculate pmf of sum of n identical discrete uniform random variables. Return values as dict.

Adapted from https://stackoverflow.com/a/69842911/3453768.

Parameters
  • n (int) – Number of uniform random variables in the sum.

  • lo (int) – Lower bound of uniform distribution.

  • hi (int) – Upper bound of uniform distribution.

Returns

Dictionary of pmf values.

Return type

dict

Raises

ValueError – If n is not an integer.

sum_of_discrete_uniforms_distribution(n, lo, hi)[source]

Return distribution of sum of n identical discrete uniform random variables as an rv_continuous object.

Parameters
  • n (int) – Number of uniform random variables in the sum.

  • lo (int) – Lower bound of uniform distribution.

  • hi (int) – Upper bound of uniform distribution.

Returns

distribution – The rv_discrete object.

Return type

rv_discrete

Raises

ValueError – If n is not an integer.

sum_of_discretes_distribution(n, lo, hi, p)[source]

Return distribution of convolution of n identical discrete random variables as an rv_discrete object.

The random variables must have support lo, lo + 1, …, hi. (The convolution will have support n * lo, n * lo + 1, …, n * hi).

Parameters
  • n (int) – Number of uniform random variables in the sum.

  • lo (int) – Smallest value of the support of the random variable.

  • hi (int) – Largest value of the support of the random variable.

  • p (list) – Probabilities of each of the values.

Returns

distribution – The rv_discrete object.

Return type

rv_discrete

Raises
  • ValueError – If n, lo, or hi are not integers.

  • ValueError – If p does not have length hi - lo + 1.

round_dict_values(the_dict, round_type=None)[source]

Round the values in a dict. Return a new dict.

Parameters
  • the_dict (dict) – The dict to round

  • round_type (string, optional) – Set to ‘up’ to round all values up to next larger integer, ‘down’ to round down, ‘nearest’ to round to nearest integer, or None to not round at all.