def get_atoms_4(self, ii, jj): # ii = self.ii # jj = self.jj natoms = self.natoms adj = np.asarray([ii, jj]) adj2 = adj.copy() adj2[0, :] = adj[1, :] adj2[1, :] = adj[0, :] adjacencymatrix = np.concatenate([adj, adj2], axis=1) ii = adjacencymatrix[0, :] jj = adjacencymatrix[1, :] # ii = ii - 1 # jj = jj - 1 # known adjacencies molecularadjacencymatrix = sparse.csr_matrix((np.ones(len(ii)), (ii, jj))) # compute atomic tetrahedra with central atoms in middle two coordinates atoms4 = np.ones((1, 4)) for i in range(natoms): nebs = molecularadjacencymatrix[i].indices # nnebs = len(molecularadjacencymatrix[i].indices) for j in nebs: if j > i: i1s = np.setdiff1d(molecularadjacencymatrix[i].indices, j) j1s = np.setdiff1d(molecularadjacencymatrix[j].indices, i) for j1 in j1s: for i1 in i1s: atom4 = np.reshape(np.asarray([i1, i, j, j1, ]), (1, 4)) atoms4 = np.concatenate((atoms4, atom4), axis=0) atoms4 = atoms4[1:atoms4.shape[0], :] atoms4 = np.asarray(atoms4, dtype=int) return (atoms4, atoms4.shape[0])
def runJointParentChild( self ): initial_dist, transition_dists, emission_dist = self.generateDists() graphs = self.graphs msg = self.msg msg.updateParams( initial_dist, transition_dists, emission_dist, graphs ) print( 'About to filter' ) U, V = msg.filter() print( '\nJoint parent child should marginalize out to joint probs' ) for n, probs in msg.jointParentChild( U, V, msg.nodes ): parents, parent_order = msg.getParents( n, get_order=True ) n_parents = parents.shape[ 0 ] joints = msg.nodeJoint( U, V, parents ) for i, ( ( p, j ), o ) in enumerate( zip( joints, parent_order ) ): # Marginalize out the other parents from probs int_axes = np.setdiff1d( np.hstack( ( n_parents, parent_order ) ), o ) reduced = msg.integrate( probs, axes=int_axes ) print( 'sum_{ parents except %d }P( x_%d, x_p1..pN, Y ) for node %d - P( x_%d, Y ) : ->'%( p, p, n, p ), ( j - reduced ).sum() ) assert np.allclose( reduced, j ), 'reduced: %s, j: %s'%( reduced, j ) ( _, joint ), = msg.nodeJoint( U, V, [ n ] ) # Marginalize out all of the parents reduced = msg.integrate( probs, axes=parent_order ) print( 'sum_{ parents }P( x_%d, x_p1..pN, Y ) - P( x_%d, Y ) : ->'%( n, n ), ( joint - reduced ).sum() ) assert np.allclose( reduced, joint ), 'reduced: %s, j: %s'%( reduced, j )
def initialize_W(Xhat, Yhat, scale_by=0.2, allow_s2=True): nP = Xhat[0][0].shape[1] nQ = Yhat[0][0].shape[1] nN = Yhat[0][0].shape[0] YYhat = calnet.utils.flatten_nested_list_of_2d_arrays(Yhat) XXhat = calnet.utils.flatten_nested_list_of_2d_arrays(Xhat) Wmy0 = np.zeros((nQ, nQ)) Wmx0 = np.zeros((nP, nQ)) #Ymatrix_pred = np.zeros((nN,nQ)) Ymatrix = np.zeros((nN, nQ)) for itype in range(nQ): if allow_s2: Ymatrix[:, itype] = invert_f_mt(YYhat[:, itype]) # nN else: Ymatrix[:, itype] = invert_f_mt(YYhat[:, itype], s02=0) # nN others = np.setdiff1d(np.arange(nQ), itype) # nQ Xmatrix = np.concatenate((XXhat[:, :nP], YYhat[:, others]), axis=1) # nN,nP+(nQ-1) Bmatrix = np.linalg.pinv( Xmatrix ) @ Ymatrix[:, itype] # B = X^+Y, where X is (X,Y), Y is Eta, and B is W # solution to Y = XB Wmx0[:, itype] = Bmatrix[:nP] Wmy0[others, itype] = Bmatrix[nP:] #Ymatrix_pred[:,itype] = Xmatrix @ Bmatrix return scale_by * Wmx0, scale_by * Wmy0
def m_step(self, expectations, datas, inputs, masks, tags, optimizer="adam", num_iters=10, **kwargs): """ Fit a logistic regression for the transitions. Technically, this is a stochastic M-step since the states are sampled from their posterior marginals. """ K, M, D = self.K, self.M, self.D zps, zns = [], [] for Ez, _, _ in expectations: z = np.array([np.random.choice(K, p=p) for p in Ez]) zps.append(z[:-1]) zns.append(z[1:]) X = np.vstack([ np.hstack((input[1:], data[:-1])) for input, data in zip(inputs, datas) ]) y = np.concatenate(zns) # Identify used states used = np.unique(y) K_used = len(used) unused = np.setdiff1d(np.arange(K), used) # Reset parameters before filling in self.Ws = np.zeros((K, M)) self.Rs = np.zeros((K, D)) self.r = np.zeros((K, )) if K_used == 1: warn( "RecurrentOnlyTransitions: Only using 1 state in expectation. " "M-step cannot proceed. Resetting transition parameters.") return # Fit the logistic regression self._lr.fit(X, y) # Extract the coefficients assert self._lr.coef_.shape[0] == (K_used if K_used > 2 else 1) if K_used == 2: # lr thought there were only two classes self.Ws[used[1]] = self._lr.coef_[0, :M] self.Rs[used[1]] = self._lr.coef_[0, M:] else: self.Ws[used] = self._lr.coef_[:, :M] self.Rs[used] = self._lr.coef_[:, M:] # Set the intercept self.r[used] = self._lr.intercept_
def scatter1d(nonzero_values, nonzero_indices, array_len): all_indices = np.arange(array_len, dtype=anp.int64) zero_indices = anp.setdiff1d(all_indices, nonzero_indices, assume_unique=True) index_map = inverse_permutation( anp.concatenate([nonzero_indices, zero_indices])) u_values = anp.concatenate([nonzero_values, anp.zeros(len(zero_indices))]) return u_values[index_map]
def initialProb(self, node): pi = np.copy(self.pi0) if (int(node) in self.possible_latent_states): states = self.possible_latent_states[int(node)] impossible_states = np.setdiff1d(np.arange(pi.shape[-1]), states) for state in impossible_states: pi[state] = np.NINF pi[states] -= logsumexp(pi) return pi
def m_step(self, expectations, datas, inputs, masks, tags, optimizer="adam", num_iters=10, **kwargs): """ Fit a logistic regression for the transitions. Technically, this is a stochastic M-step since the states are sampled from their posterior marginals. """ from sklearn.linear_model import LogisticRegression K, M, D = self.K, self.M, self.D zps, zns = [], [] for Ez, _ in expectations: z = np.array([np.random.choice(K, p=p) for p in Ez]) zps.append(z[:-1]) zns.append(z[1:]) X = np.vstack([np.hstack((one_hot(zp, K), input[1:], data[:-1])) for zp, input, data in zip(zps, inputs, datas)]) y = np.concatenate(zns) # Determine the number of states used used = np.unique(y) K_used = len(used) unused = np.setdiff1d(np.arange(K), used) # Reset parameters before filling in self.log_Ps = np.zeros((K, K)) self.Ws = np.zeros((K, M)) self.Rs = np.zeros((K, D)) if K_used == 1: warn("RecurrentTransitions: Only using 1 state in expectation. " "M-step cannot proceed. Resetting transition parameters.") return # Fit the logistic regression lr = LogisticRegression(fit_intercept=False, multi_class="multinomial", solver="sag") lr.fit(X, y) # Extract the coefficients assert lr.coef_.shape[0] == (K_used if K_used > 2 else 1) log_P = lr.coef_[:, :K] W = lr.coef_[:, K:K+M] R = lr.coef_[:, K+M:] if K_used == 2: # lr thought there were only two classes self.log_Ps[:,used[1]] = lr.coef_[0, :K] self.Ws[used[1]] = lr.coef_[0,K:K+M] self.Rs[used[1]] = lr.coef_[0,K+M:] else: self.log_Ps[:, used] = log_P.T self.Ws[used] = W self.Rs[used] = R
def get_percentile_calibration_likelihood(filenames, model, interval_coverage=90, method='IJ', inds=None): ''' Currently checks percentile estimates over each dimension of the parameters independently (so we only have to compute precentiles over 1-D things) true_params should be a D dimensional array interval_coverage specifies the size of the interval around the median; i.e. 95 corresponds to the interval [2.5%, 97.5%] ''' nExp = len(filenames) in_range = np.zeros(nExp, dtype=np.bool) for n in range(nExp): bs_run = load_run(filenames[n]) true_params = bs_run['multinomial']['truth']['theta'] if method == 'linear_approx': bs_samples = bs_run['multinomial']['bootstrap_params_appx'].copy() elif method == 'exact': bs_samples = bs_run['multinomial']['bootstrap_params_exact'].copy() model.training_data = bs_run['train_data'] if inds is not None: fixed_inds = np.setdiff1d(np.arange(true_params.shape[0]), inds) bs_samples[:,fixed_inds] = np.mean(bs_samples, axis=0)[fixed_inds] true_like = model.eval_objective(true_params) likelihood_interval = get_likelihood_based_interval(bs_samples, interval_coverage, model) if (likelihood_interval.min() <= true_like and true_like <= likelihood_interval.max()): in_range[n] = True else: in_range[n] = False ''' if inds is None: conv_hull = scipy.spatial.Delaunay(params_in_interval) in_range[n] = conv_hull.find_simplex(true_params) > 0 else: if len(inds) > 1: conv_hull = scipy.spatial.Delaunay(params_in_interval[:,inds]) in_range[n] = conv_hull.find_simplex(true_params[inds]) > 0 else: in_range[n] = ((params_in_interval[:,inds].min() < true_params[inds][0]) and (params_in_interval[:,inds].max() > true_params[inds][0])) ''' return in_range
def initialProb(self, node, is_partial_graph_index=False): pi = np.copy(self.pi0) node_full = self.partialGraphIndexToFullGraphIndex( node) if is_partial_graph_index == True else node if (int(node_full) in self.possible_latent_states): states = self.possible_latent_states[int(node_full)] impossible_states = np.setdiff1d(np.arange(pi.shape[-1]), states) for state in impossible_states: pi[impossible_states] = np.NINF pi[states] -= logsumexp(pi) return fbsData(pi, -1)
def fbsTests(): graphs = [ graph1(), graph2(), graph3(), graph4(), graph5(), graph6(), graph7(), cycleGraph1(), cycleGraph2(), cycleGraph3(), cycleGraph7(), cycleGraph8(), cycleGraph9(), cycleGraph10(), cycleGraph11(), cycleGraph12() ] # graphs = [ cycleGraph12() ] msg = GraphMessagePasserFBS() msg.updateGraphs(graphs) msg.draw(styles={0: dict(style='filled', color='red')}, node_to_style_key=dict([(n, 0) for n in msg.fbs])) def nothing(is_base_case, node_list): return count = 0 def loopyHasConverged(): nonlocal count count += 1 return count > 3 msg.upDown(nothing, nothing, loopyHasConverged=loopyHasConverged) class worker(): def __init__(self): self.visited = [] def __call__(self, node_list): self.visited.append(node_list) work = worker() msg.forwardPass(work) visited = np.hstack(work.visited) diff = np.setdiff1d(msg.nodes, visited) assert diff.size == 0 print('Done with the improved fbs message passing tests!')
def transitionProb(self, child): parents, parent_order = self.getParents(child, get_order=True) ndim = len(parents) + 1 pi = np.copy(self.pis[ndim]) # If we know the latent state for child, then ensure that we # transition there. Also make sure we're only using the possible # parent latent states!!!! modified = False for parent, order in zip(parents, parent_order): if (int(parent) in self.possible_latent_states): parent_states = self.possible_latent_states[int(parent)] impossible_parent_axes = np.setdiff1d( np.arange(pi.shape[order]), parent_states) index = [slice(0, s) for s in pi.shape] index[order] = impossible_parent_axes pi[tuple(index)] = np.NINF modified = True if (int(child) in self.possible_latent_states): child_states = self.possible_latent_states[int(child)] impossible_child_axes = np.setdiff1d(np.arange(pi.shape[-1]), child_states) pi[..., impossible_child_axes] = np.NINF modified = True if (modified == True): with np.errstate(invalid='ignore'): pi[..., :] -= logsumexp(pi, axis=-1)[..., None] # In case entire rows summed to -inf pi[np.isnan(pi)] = np.NINF # Reshape pi's axes to match parent order assert len(parents) + 1 == pi.ndim assert parent_order.shape[0] == parents.shape[0] pi = np.moveaxis(pi, np.arange(ndim), np.hstack((parent_order, ndim - 1))) return pi
def compute_restricted_hessian_and_dParamsdWeights(self, dims, weights, comp_dParams_dWeights=True): ''' Computes the dims.shape[0] by dims.shape[0] Hessian only along the entries in dims (used when using l_1 regularization) ''' theta0 = self.params.get_free() # Objective to differentiate just along the dimensions specified def lowDimObj(weights, thetaOnDims, thetaOffDims, invPerm): allDims = np.append(dims, offDims) thetaFull = np.append(thetaOnDims, thetaOffDims)[invPerm] return self.weighted_model_objective(weights, thetaFull) offDims = np.setdiff1d(np.arange(self.params.get_free().shape[0]), dims) thetaOnDims = theta0[dims] thetaOffDims = theta0[offDims] # lowDimObj will concatenate thetaOnDims, thetaOffDims, then needs to # un-permute them into the original theta. allDims = np.append(dims, offDims) invPerm = np.zeros(theta0.shape[0], dtype=np.int32) for i, idx in enumerate(allDims): invPerm[idx] = i evalHess = autograd.hessian(lowDimObj, argnum=1) array_box_go_away = self.params.get_free().copy() restricted_hess = evalHess(weights, thetaOnDims, thetaOffDims, invPerm) self.params.set_free(theta0) dObj_dParams = autograd.jacobian(lowDimObj, argnum=1) d2Obj_dParamsdWeights = autograd.jacobian(dObj_dParams, argnum=0) if comp_dParams_dWeights: restricted_dParamsdWeights = d2Obj_dParamsdWeights(weights, thetaOnDims, thetaOffDims, invPerm) return restricted_hess, restricted_dParamsdWeights else: return restricted_hess
def initialize_W(Xhat, Yhat, scale_by=0.2): nP = Xhat[0][0].shape[1] nQ = Yhat[0][0].shape[1] nN = Yhat[0][0].shape[0] YYhat = calnet.utils.flatten_nested_list_of_2d_arrays(Yhat) XXhat = calnet.utils.flatten_nested_list_of_2d_arrays(Xhat) Wmy0 = np.zeros((nQ, nQ)) Wmx0 = np.zeros((nP, nQ)) Ymatrix = np.zeros((nN, nQ)) for itype in range(nQ): Ymatrix[:, itype] = invert_f_mt(YYhat[:, itype]) others = np.setdiff1d(np.arange(nQ), itype) Xmatrix = np.concatenate((XXhat[:, :nP], YYhat[:, others]), axis=1) Bmatrix = np.linalg.pinv(Xmatrix) @ Ymatrix[:, itype] Wmx0[:, itype] = Bmatrix[:nP] Wmy0[others, itype] = Bmatrix[nP:] #Ymatrix_pred[:,itype] = Xmatrix @ Bmatrix return scale_by * Wmx0, scale_by * Wmy0
def test_replace(self): dim = 10 x = np.random.random(dim) x_orig = copy.deepcopy(x) inds = [1, 3, 5] x_sub = np.random.random(len(inds)) non_inds = np.setdiff1d(np.arange(dim), inds) x_new = autograd_supplement_lib.replace(x_sub, x, inds) assert_array_almost_equal(x_new[inds], x_sub) assert_array_almost_equal(x_new[non_inds], x[non_inds]) assert_array_almost_equal(x, x_orig) assert len(x_new) == len(x) # Apparnetly check grads doesn't check all the arguments? check_grads( lambda x_sub: autograd_supplement_lib.replace(x_sub, x, inds))(x_sub) check_grads( lambda x: autograd_supplement_lib.replace(x_sub, x, inds))(x)
def TrainingCurve(fig, config_file): args = GetTrainedEmulator(config_file) clf = args[0] prior = args[1] exp_Y = args[2] exp_Yerr = args[3] model_X = args[4].values model_Y = args[5].values training_idx = args[6] validation_idx = np.setdiff1d(np.arange(model_X.shape[0]), training_idx) history_para = args[7].fillna(method="ffill") clf.Fit(model_X, model_Y) axes = fig.subplots(1, 2) NumberOfPts(axes[0], clf, model_X, model_Y, training_idx, validation_idx) NumberOfSteps( axes[1], clf, model_X, model_Y, training_idx, validation_idx, history_para)
def nodeJointSingleNode( self, U, V, node, method='compute', is_partial_graph_index=False ): # P( x, Y ) # Having different methods is more of a sanity check than anything not_in_fbs = not self.inFeedbackSet( node, is_partial_graph_index=is_partial_graph_index ) if( not_in_fbs and method == 'integrate' ): # Can't use integrate method if node isn't in the fbs method = 'compute' # If the fbs node is a leaf, must use the integrate method if( not_in_fbs == False and self.nParents( node, is_partial_graph_index=is_partial_graph_index, use_partial_graph=False ) == 0 ): method = 'integrate' # Also if the fbs node has all fbs node parents, must use integrate method if( not_in_fbs == False and len( [ 1 for p in self.getFullParents( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=False ) if not self.inFeedbackSet( p, is_partial_graph_index=False ) ] ) == 0 ): method = 'integrate' if( method == 'compute' ): # Need to use the partial graph index from here node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node # If the node isn't in the fbs, compute the joint the normal way. if( not_in_fbs ): u = self.uData( U, V, node_partial ) vs = self.vData( U, V, node_partial ) vs = [ self.extendAxes( node_partial, v, 0, 1 ) for v in vs ] joint_fbs = self.multiplyTerms( terms=( u, *vs ) ) int_axes = range( 1, joint_fbs.ndim ) else: # Just computing u for the fbs should, by construction, # cover all of the nodes in the graph. Still need to integrate # out other fbs nodes in this case joint_fbs = self.u( U, V, node_partial ) fbs_index = self.fbsIndex( node, is_partial_graph_index=is_partial_graph_index, within_graph=True ) keep_axis = fbs_index + joint_fbs.fbs_axis int_axes = np.setdiff1d( np.arange( joint_fbs.ndim ), keep_axis ) return self.integrate( joint_fbs, axes=int_axes ).data elif( method == 'integrate' ): # If node is in the fbs, choose another node to get the joint with the fbs node with. # Otherwise, calculate the joint the regular way if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: node_partial = self._anotherNode( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) # Compute the joint using the regular algorithm but on the partial_graph joint_fbs = self.nodeJointSingleNodeComputation( U, V, node_partial ) if( not_in_fbs ): # Integrate out all of the parents and fbs nodes int_axes = range( 1, joint_fbs.ndim ) else: # Integrate out all the nodes but this one, which is somewhere in the fbs axes fbs_index = self.fbsIndex( node, is_partial_graph_index=is_partial_graph_index, within_graph=True ) keep_axis = fbs_index + joint_fbs.fbs_axis int_axes = np.setdiff1d( np.arange( joint_fbs.ndim ), keep_axis ) # Integrate the joint and return only the data portion return self.integrate( joint_fbs, axes=int_axes ).data else: assert 0, 'Invalid method'
def fit(self, X, Y, iterations, batch_size = False, learning_parameters = default_parameters, show_it = 100, record_hist = False): learning_parameters = {**default_parameters, **learning_parameters} learning_rate = learning_parameters["LR"] type_epsilon = learning_parameters["type_epsilon"] reg = learning_parameters["reg"] learning_parameters["rate"] = learning_rate/iterations loss_name = learning_parameters["loss"] grad = grad_dic[loss_name] batch_creation = sampling_dic[learning_parameters["sampling"]] # Create a copy of the parameters (so the original parameters aren't modified) self.LR = [learning_rate] self.type_epsilon = type_epsilon self.X_train = np.copy(X) self.Y_train = np.copy(Y) self.iteration = iterations self.points_hist.append(np.copy(X)) parameters = self.parameters X = np.copy(X) self.X = X data_set_ind = np.arange(X.shape[0]) perturbation = np.zeros(X.shape) for i in range(iterations): if type(show_it) == int and i % show_it == 0: print("Iteration ", i) # Create a batch and a sample sample_indices, batch_indices = batch_creation(X, batch_size) X_batch = X[batch_indices] Y_batch = Y[batch_indices] self.batch_hist.append(np.copy(X_batch)) # The indices of all the elements not in the batch not_batch = np.setdiff1d(data_set_ind, batch_indices) # Compute the gradient rho, g = grad(self.parameters, X_batch, Y_batch, sample_indices, self.kernel_keyword, reg = reg) g = -g if loss_name == "rho" and (rho >1.0001 or rho <-0.00001): print ("Rho outside allowed bounds", rho) # Compute the perturbations by interpolation if batch_size == False: perturbation = g coeff = kernel_regression_coeff(X_batch, g, parameters, self.kernel_keyword, reg = reg) else: g_interpolate, coeff = kernel_regression(X_batch, X[not_batch], g, parameters, self.kernel_keyword, reg = reg) perturbation[batch_indices] = g perturbation[not_batch] = g_interpolate #Find epsilon epsilon = compute_LR(learning_rate, X, perturbation, type_epsilon = type_epsilon) # Adjust the learning rate based on the learning parameters learning_rate = adjust_LR(i, learning_rate, learning_parameters) self.LR.append(learning_rate) #Update the points X += epsilon * perturbation # Recording the regression coefficients self.coeff.append(coeff) # Update the history self.rho_values.append(rho) self.epsilon.append(epsilon) self.perturbation.append(perturbation) if record_hist == True: self.points_hist.append(np.copy(X)) return X
def jointParentsSingleNode( self, U, V, node, method='compute', is_partial_graph_index=False ): # P( x_p1..pN, Y ) not_in_fbs = not self.inFeedbackSet( node, is_partial_graph_index=is_partial_graph_index ) if( method == 'integrate' ): if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: node_partial = self._anotherNode( node, parents=False, mates=False, children=False, siblings=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) # Couldn't find a sibling, so use the compute method if( node_partial is None ): method = 'compute' # Can't use integrate method if node isn't in the fbs if( not_in_fbs ): method = 'compute' if( len( [ 1 for p in self.getFullParents( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=False ) if not self.inFeedbackSet( p, is_partial_graph_index=False ) ] ) == 0 ): if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: # Find any node to run the nodeJoint algorithm on node_partial = self._anotherNode( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) joint_fbs = self.nodeJointSingleNodeComputation( U, V, node_partial ) elif( method == 'compute' ): # Regular computation node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node joint_fbs = self.jointParentsSingleNodeComputation( U, V, node_partial ) elif( method == 'integrate' ): # Find a sibling to pass node_partial = self._anotherNode( node, parents=False, mates=False, children=False, siblings=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) joint_fbs = self.jointParentsSingleNodeComputation( U, V, node_partial ) else: assert 0, 'Invalid method' # Loop through the parents and find the axes that they are on so we can keep them parents, parent_order = self.getFullParents( node, get_order=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) keep_axes = [] for p, o in zip( parents, parent_order ): if( self.inFeedbackSet( p, is_partial_graph_index=True ) ): keep_axes.append( joint_fbs.fbs_axis + self.fbsIndex( p, is_partial_graph_index=True, within_graph=True ) ) else: keep_axes.append( o ) keep_axes = np.array( keep_axes ) # Integrate out all fbs nodes that aren't the parents int_axes = np.setdiff1d( np.arange( joint_fbs.ndim ), keep_axes ) ans = self.integrate( joint_fbs, axes=int_axes ).data # Swap the order of the axes so that the answer has the correct parent order. # At the moment, all of the fbs indices are on the last indices sorted by # fbs index non_fbs_order = [ o for p, o in zip( parents, parent_order ) if not self.inFeedbackSet( p, is_partial_graph_index=True ) ] fbs_parents = [ ( p, o ) for p, o in zip( parents, parent_order ) if self.inFeedbackSet( p, is_partial_graph_index=True ) ] fbs_order = sorted( fbs_parents, key=lambda x: self.fbsIndex( x[ 0 ], is_partial_graph_index=True, within_graph=True ) ) fbs_order = [ o for p, o in fbs_order ] true_order = non_fbs_order + fbs_order transpose_axes = [ true_order.index( i ) for i in range( len( true_order ) ) ] return np.transpose( ans, transpose_axes )
def jointParentChildSingleNode( self, U, V, node, method='compute', is_partial_graph_index=False ): # P( x_c, x_p1..pN, Y ) not_in_fbs = not self.inFeedbackSet( node, is_partial_graph_index=is_partial_graph_index ) if( method == 'integrate' ): # This fbs node has no siblings, so must compute if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: node_partial = self._anotherNode( node, parents=False, mates=False, children=False, siblings=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) # Couldn't find a sibling, so use the compute method if( node_partial is None ): method = 'compute' # Can't use integrate method if node isn't in the fbs if( not_in_fbs ): method = 'compute' # If all of the parents are in the fbs and so is node, then use nodeJoint and integrate out the correct nodes. if( len( [ 1 for p in self.getFullParents( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) if not self.inFeedbackSet( p, is_partial_graph_index=True ) ] ) == 0 ): if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: # Find any node to run the nodeJoint algorithm on node_partial = self._anotherNode( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) joint_fbs = self.nodeJointSingleNodeComputation( U, V, node_partial ) elif( method == 'compute' ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node joint_fbs = self.jointParentChildSingleNodeComputation( U, V, node_partial ) elif( method == 'integrate' ): if( not_in_fbs ): node_partial = self.fullGraphIndexToPartialGraphIndex( node ) if is_partial_graph_index == False else node else: # Find a sibling to run the algorithm on node_partial = self._anotherNode( node, parents=False, mates=False, children=False, siblings=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) joint_fbs = self.jointParentChildSingleNodeComputation( U, V, node_partial ) else: assert 0, 'Invalid method' # Loop through the parents and find the axes that they are on so we can keep them parents, parent_order = self.getFullParents( node, get_order=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) n_parents = self.nParents( node, is_partial_graph_index=is_partial_graph_index, use_partial_graph=False ) keep_axes = [] for p, o in zip( parents, parent_order ): if( self.inFeedbackSet( p, is_partial_graph_index=True ) ): keep_axes.append( joint_fbs.fbs_axis + self.fbsIndex( p, is_partial_graph_index=True, within_graph=True ) ) else: keep_axes.append( o ) # Keep the axis that node is on if( not_in_fbs ): n_fbs_parents = len( [ 1 for p in self.getFullParents( node, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True ) if self.inFeedbackSet( p, is_partial_graph_index=True ) ] ) if( n_fbs_parents == n_parents ): keep_axes.append( 0 ) else: keep_axes.append( n_parents ) else: keep_axes.append( joint_fbs.fbs_axis + self.fbsIndex( node, is_partial_graph_index=is_partial_graph_index, within_graph=True ) ) keep_axes = np.array( keep_axes ) # Integrate out all fbs nodes that aren't the parents int_axes = np.setdiff1d( np.arange( joint_fbs.ndim ), keep_axes ) ans = self.integrate( joint_fbs, axes=int_axes ).data # Swap the order of the axes to that the answer has the correct parent order # and so that the node is on the last axis. To do this, first find the true # axes that each of the current axes correspond to non_fbs_order = [ o for p, o in zip( parents, parent_order ) if not self.inFeedbackSet( p, is_partial_graph_index=True ) ] fbs_parents = [ ( p, o ) for p, o in zip( parents, parent_order ) if self.inFeedbackSet( p, is_partial_graph_index=True ) ] fbs_order = sorted( fbs_parents, key=lambda x: self.fbsIndex( x[ 0 ], is_partial_graph_index=True, within_graph=True ) ) fbs_order = [ o for p, o in fbs_order ] # If node is in the fbs, then that means that its current axis is somewhere # after the fbs_axis. So place the node's axis correctly if( self.inFeedbackSet( node, is_partial_graph_index=is_partial_graph_index ) ): insertion_index = self.fbsIndex( node, is_partial_graph_index=is_partial_graph_index, within_graph=True ) true_order = non_fbs_order + fbs_order[ :insertion_index ] + [ len( parents ) ] + fbs_order[ insertion_index: ] else: true_order = non_fbs_order + [ len( parents ) ] + fbs_order # At the moment, true_order corresonds to the axis that each of the current # axes should map to. np.transpose expects the opposite of this transpose_axes = [ true_order.index( i ) for i in range( len( true_order ) ) ] return np.transpose( ans, transpose_axes )
def transitionProb(self, child, is_partial_graph_index=False): parents, parent_order = self.getFullParents( child, get_order=True, is_partial_graph_index=is_partial_graph_index, return_partial_graph_index=True) child_full = self.partialGraphIndexToFullGraphIndex( child) if is_partial_graph_index == True else child ndim = len(parents) + 1 group = self.node_groups[int(child_full)] shape = [] for p, _ in sorted(zip(parents, parent_order), key=lambda po: po[1]): full_p = int(self.partialGraphIndexToFullGraphIndex(p)) shape.append(self.getNodeDim(full_p)) shape.append(self.getNodeDim(int(child_full))) shape = tuple(shape) pi = np.copy(self.pis[group][shape]) # Reshape pi's axes to match parent order assert len(parents) + 1 == pi.ndim assert parent_order.shape[0] == parents.shape[0] # Sort the parent dimensions by parent order pi = np.moveaxis(pi, np.arange(ndim), np.hstack((parent_order, ndim - 1))) # If we know the latent state for child, then ensure that we # transition there. This is intervention! modified = False for parent, order in zip(parents, parent_order): parent_full = self.partialGraphIndexToFullGraphIndex(parent) if (int(parent_full) in self.possible_latent_states): parent_states = self.possible_latent_states[int(parent_full)] impossible_parent_axes = np.setdiff1d( np.arange(pi.shape[order]), parent_states) index = [slice(0, s) for s in pi.shape] index[order] = impossible_parent_axes pi[tuple(index)] = np.NINF modified = True if (int(child_full) in self.possible_latent_states): states = self.possible_latent_states[int(child_full)] impossible_axes = np.setdiff1d(np.arange(pi.shape[-1]), states) pi[..., impossible_axes] = np.NINF modified = True # In case entire rows summed to -inf pi[np.isnan(pi)] = np.NINF # Check if there are nodes in [ child, *parents ] that are in the fbs. # If there are, then move their axes fbsOffset = lambda x: self.fbsIndex( x, is_partial_graph_index=True, within_graph=True) + 1 fbs_indices = [ fbsOffset(parent) for parent in parents if self.inFeedbackSet(parent, is_partial_graph_index=True) ] if (self.inFeedbackSet(child, is_partial_graph_index=is_partial_graph_index)): fbs_indices.append( self.fbsIndex(child, is_partial_graph_index=is_partial_graph_index, within_graph=True) + 1) if (len(fbs_indices) > 0): expand_by = max(fbs_indices) for _ in range(expand_by): pi = pi[..., None] # If there are parents in the fbs, move them to the appropriate axes for i, parent in enumerate(parents): if (self.inFeedbackSet(parent, is_partial_graph_index=True)): pi = np.swapaxes(pi, i, fbsOffset(parent) + ndim - 1) if (self.inFeedbackSet( child, is_partial_graph_index=is_partial_graph_index)): # If the child is in the fbs, then move it to the appropriate axis pi = np.swapaxes(pi, ndim - 1, fbsOffset(child) + ndim - 1) return fbsData(pi, ndim) return fbsData(pi, -1)
# 10-fold cross-validation setup nt = np.floor(cvind.shape[0] / 10).astype(int) cvind = np.reshape(cvind[:10 * nt], (10, nt)) # Array of NLPDs nlpd = [] # Run for each fold for fold in np.arange(10): print(100 * '-') print('Fold %d' % fold) print(100 * '-') # Get training and test indices test = cvind[fold, :] train = np.setdiff1d(cvind, test) # Set training and test data X = Xall[train, :] y = yall[train, :] Xt = Xall[test, :] yt = yall[test, :] #. Run results res = run_fold(X, y, Xt, yt) nlpd.append(res) print(res) print(np.mean(nlpd), np.std(nlpd))
def run( self ): initial_dist, transition_dists, emission_dist = self.generateDists() graphs = self.graphs msg = self.msg msg.updateParams( initial_dist, transition_dists, emission_dist, graphs ) msg.draw() U, V = msg.filter() assert 0 # for node, elt in enumerate( U ): # node = msg.partialGraphIndexToFullGraphIndex( node ) # print( 'node', node, 'elt.shape', elt.shape, 'elt.fbs_axis', elt.fbs_axis ) # for node, edge, elt in zip( *V ): # node = msg.partialGraphIndexToFullGraphIndex( node ) # print( 'node', node, 'edge', edge, 'elt.shape', elt.shape, 'elt.fbs_axis', elt.fbs_axis ) # assert 0 #################################################### print( '\nJoint' ) for n, probs in msg.nodeJoint( U, V, msg.nodes ): reduced = msg.integrate( probs, axes=range( probs.ndim ) ) print( 'P( x_%d, Y )'%( n ), ':', probs, '->', reduced ) #################################################### print( '\nJoint parents should marginalize out to joint probs' ) for n, probs in msg.jointParents( U, V, msg.nodes ): parents, parent_order = msg.getParents( n, get_order=True ) joints = msg.nodeJoint( U, V, parents ) for i, ( ( p, j ), o ) in enumerate( zip( joints, parent_order ) ): # Marginalize out the other parents from probs int_axes = np.setdiff1d( parent_order, o ) reduced = msg.integrate( probs, axes=int_axes ) print( 'sum_{ parents except %d }P( x_p1..pN, Y ) for node %d - P( x_%d, Y ) : ->'%( p, n, p ), ( j - reduced ).sum() ) assert np.allclose( reduced, j ), 'reduced: %s, j: %s'%( reduced, j ) #################################################### print( '\nJoint parent child should marginalize out to joint probs' ) for n, probs in msg.jointParentChild( U, V, msg.nodes ): parents, parent_order = msg.getParents( n, get_order=True ) n_parents = parents.shape[ 0 ] joints = msg.nodeJoint( U, V, parents ) for i, ( ( p, j ), o ) in enumerate( zip( joints, parent_order ) ): # Marginalize out the other parents from probs int_axes = np.setdiff1d( np.hstack( ( n_parents, parent_order ) ), o ) reduced = msg.integrate( probs, axes=int_axes ) print( 'sum_{ parents except %d }P( x_%d, x_p1..pN, Y ) for node %d - P( x_%d, Y ) : ->'%( p, p, n, p ), ( j - reduced ).sum() ) assert np.allclose( reduced, j ), 'reduced: %s, j: %s'%( reduced, j ) ( _, joint ), = msg.nodeJoint( U, V, [ n ] ) # Marginalize out all of the parents reduced = msg.integrate( probs, axes=parent_order ) print( 'sum_{ parents }P( x_%d, x_p1..pN, Y ) - P( x_%d, Y ) : ->'%( n, n ), ( joint - reduced ).sum() ) assert np.allclose( reduced, joint ), 'reduced: %s, j: %s'%( reduced, j ) #################################################### print( '\nSmoothed' ) for n, probs in msg.nodeSmoothed( U, V, msg.nodes ): # If we reduce over the last axis, we should have everything sum to 1 reduced = msg.integrate( probs, axes=[ -1 ] ) print( 'P( x_%d | Y )'%( n ), ':', probs, '->', probs.shape, reduced ) assert np.allclose( reduced, 0.0 ), 'Failed test!' #################################################### print( '\nChild given parents' ) for n, probs in msg.conditionalParentChild( U, V, msg.nodes ): # If we reduce over the last axis, we should have everything sum to 1 reduced = msg.integrate( probs, axes=[ -1 ] ) print( 'P( x_%d | x_p1..pN, Y )'%( n ), '->', probs.shape, reduced ) assert np.allclose( reduced, 0.0 ), 'Failed test!'