Utilities¶
Persistence (metadata, RNG, weights, topology, taus, targets), dataset slicing, the complexity and mutual-information measures, the time-constant sampler, and the score aggregation the figures are built from.
- hetero.utils.get_default_args(func)[source]¶
Gets the default arguments of a function
- Parameters:
func (callable) – the function to be investigated
- Returns:
dict – default arguments of the provided function
- class hetero.utils.NpEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)[source]¶
Bases:
JSONEncoder- default(obj)[source]¶
Implement this method in a subclass such that it returns a serializable object for
o, or calls the base implementation (to raise aTypeError).For example, to support arbitrary iterators, you could implement default like this:
def default(self, o): try: iterable = iter(o) except TypeError: pass else: return list(iterable) # Let the base class default method raise the TypeError return super().default(o)
- hetero.utils.asave(path, arr, target_type)[source]¶
a simple array saver that safely cast arrays to the target type.
- hetero.utils.compute_dataset_slices(L_trn, L_tst, base_stride, task_params)[source]¶
Note that the task_params are in the physical units and have to be multiplied by base_stride to get the steps.
- hetero.utils.compute_complexity_mi(x, y, bins=1000, return_prf=False)[source]¶
Quantifies task difficulty based on information theoric measures by treating the mapping between the input x to the output y as a communicational problem. It returns task possibility, realizability, and feasibility, deined as below:
Possibility = min (1 , H(x) / H(y) ) Realizability = I(x:y) / H(y) Feasibility = Realizability / Feasibility
Note that 0 < Realizability < Possibility < 1. Here’s the rational behind these quanitties:
Possibility checks if the information content in input x, H(x), is enough to describe the information in y, H(y). If it is not, then no matter how far we try, we cannot fully explain distribution of the output via x alone. On the contrary, y might be pretty concentrated within a small range, giving raise to a small entropy. In this case, the input x might be informative enought to describe y.
Even when y has a smaller entropy than x, they may be only partially informative of one another. Mutual information I(x:y) exactly captures this dependence. Note that by definition, I cannot exceed either channel’s entorpy. The closer I is the entropy of the output, the more informative x is about y. Realizability qunatifies this.
Now, how difficult the communication is depends on the possiblity and the realiziability. Since, realizability is bounded the the amount of possiblity, their ratio explain how feasible the task is.
Note: - The formula for mutual information is take from here:
- hetero.utils.compute_mi(x, y, bins=100, in_bits=False, rangex=None, rangey=None, verbose=True)[source]¶
Computes the discrete mutual information between two 1D variables by approximating the joint distribution with a 2D histogram.
Note that it is very important to set rangex, rangey, and bins. Because these will determine the bin size which ultimately impacts the MI. Without ranges, the bin sizes may be different for different x and y. Thus, a comparison between MIs might be misleading.
This function has been taken from https://stackoverflow.com/a/20505476/7106957. Also look at https://en.wikipedia.org/wiki/G-test#Relation_to_Kullback%E2%80%93Leibler_divergence
- hetero.utils.aggregate_scores(root, sim_pattern, invalid_patterns=None, score_name=None, return_std=True, n_tiers=3, transform=True, transforms=(lambda x: ..., lambda x: ...))[source]¶
- hetero.utils.sample_taus(size, mu, std, distr='lognormal', clip_below=0, rng=None, tol_prcntg=-1, verbose=False)[source]¶
The given mu and std are the desired mean and standard deviation and are different from the parameters passed to rng. It can also handle the case of std=0, by returning a constant vector of the desired size.
- hetero.utils.estimate_poly2_max_from_stats(mu, std, verbose=False)[source]¶
If mn and mx are the minimum and maximum values, the pdf would have the following form:
pdf(x) = 1/Z * 1/(1+x**2) (0) Z = atan(mx) - atan(mn)
And the first and second moments of this truncated pdf are:
E[x] = 1/(2*Z) * log( (1+mx**2) / (1+mn**2) ) (1) E[x**2] = (mx - mn)/Z - 1 (2)
One must find mx and mn such that:
E[x] = mu E[x**2] = std**2 - mu**2
which is very hard to solve. Fortunately, it turns out that both statistics are mostly influenced by mx and not mn. Thus we can use approximations mn ≈ 0, which gives raise to:
E[x] ≈ log( 1+ mx**2 ) / (2 * atan(mx)) (3) E[x**2] ≈ mx / atan(mx) - 1 (4)
Now, we can numerically solve for mx. However, only one of these equations can be used. The reason is that the first and second moment of the truncated pdf are not independent. Setting mu will determine std and vice versa. Here we only use equation (3). The resulting std will be reported.
- hetero.utils.poly2_sampler(mn, mx, size)[source]¶
Since numpy doesn’t have a sampler for 1/(1+x**2) distribution we use the “inverse transform sampling” method to do so. Read more about this method here:
https://en.wikipedia.org/wiki/Inverse_trigonometric_functions
- hetero.utils.fftautocorr(x)[source]¶
This implements a circular autocorrelation as opposed to the zero-padded correlation of numpy as and scipy. It is adapted from https://stackoverflow.com/a/28285527/7106957.
x must be a real sequence.
- hetero.utils.autocorr(x, maxlags=None)[source]¶
based on matplotlib’s acorr function. Always returns the normalized symmetric lag autocorrelation.
- hetero.utils.canonicalize_value(val)[source]¶
Format values consistently for filenames.
- Parameters:
val (int, float, str) – Value to format
- Returns:
str – Formatted value: - Floats in scientific notation with no + signs - Strings in uppercase - Other types stringified