""" Algebraic operations """ import csv import numpy as np from collections import OrderedDict from scipy.stats import linregress import algebra as alg import geometry as geo import workspace as ws def parameter_regressions(filename): """ Open and read a file with parameters over which to make regressions and obtain new expressions and values """ # Read data with open(filename, 'r') as f: frl = list(csv.reader(f)) keys = frl[0] data = np.array(frl[1:], dtype=np.float64) n_fields = len(keys) n_entries = len(data) data_dict = OrderedDict() for i in range(n_fields): data_dict[keys[i]] = data[:, i] # Independent variable indepvar = keys[0] # Dictionary containing the regressions regressions = OrderedDict() # Of course, indicate which one is the independent variable regressions['independent variable'] = indepvar for i, (k, v) in enumerate(list(data_dict.items())[1:]): x = data_dict[indepvar] # Linear regression slope, intercept, r_value, p_value, std_err = linregress(x, v) # Check if it's valid if intercept < 0: # Negative values, unacceptable. Use a polynomial fit # Quadratic fit parabolic_coeffs = np.polyfit(x, v, 2) if parabolic_coeffs[-1] < 0: # Not even a quadratic fit worked msg = 'ERROR: Could not obtain positive values for ' + k + ' for small ' + indepvar ws.log(msg) ws.terminate() # Give it a x-array that covers the whole domain x_ = np.linspace(0, x.max(), 100) ypol = polynomial(x_, parabolic_coeffs) # return polynomial regression a, b, c = parabolic_coeffs regressions[k] = lambda x: a * x ** 2 + b * x + c else: # Plot linear regression # Create straight line object and draw it sl = geo.StraightLine((0, intercept), slope) regressions[k] = sl.equation return regressions def polynomial(x, c): """ Get the values of y from a polynomial of degree len(c) - 1 with coefficients c, using abscissa x The coefficients c must start from the higher order, just like in np.polyfit (for consistency) """ y = np.zeros(len(x), dtype=np.float64) for i, cc in enumerate(c[::-1]): y += cc * x ** i return y def nan_to_inf(m): """ Turn all nan elements of a matrix m to infinity """ m[np.where(np.isnan(m))] = np.inf return m def diagv(v): """ Put the vector v on the diagonal of a square matrix """ eye = np.eye(v.size) r = v * eye r[np.where(eye == 0.)] = 0. return r def vectomat(v): """ Create a matrix from a vector by repeating it """ n = v.size return v.repeat(n).reshape((n, n)) def compl_mat(v): """ Obtain a particular matrix from a vector """ vm = vectomat(v) return vm - vm.T def one_side(m): """ Return a vector containing one side of the off-diagonal terms of a matrix and their indices """ # Create list of pairs nc = m.shape[0] ids = np.arange(nc) idvm = vectomat(ids) # pairs = np.array([idvm, idvm.T]).reshape((nc, nc, 2)) idvm2 = idvm.T # Create mask x = np.arange(nc) y = np.arange(nc) x, y = np.meshgrid(x, y) mask = x - y # Mask the values of m and pairs # pairs = np.ma.masked_where(mask <= 0, pairs) ids1 = np.ma.masked_where(mask <= 0, idvm) ids2 = np.ma.masked_where(mask <= 0, idvm2) m = np.ma.masked_where(mask <= 0, m) ids1 = ids1.compressed() ids2 = ids2.compressed() pairs = np.array([ids1, ids2]).T m = m.compressed() return pairs, m def make_sym_rem_nans(m): """ Given a matrix m which is supposed to be symmetric but it's not because there are nans, remove the non-due nans to make the matrix symmetric """ mmasked = np.ma.masked_where(np.isnan(m), m) mask = mmasked.mask nonan = np.where(mask.T == False) m[nonan] = mmasked.T[nonan] return m