def computeLogZ(self): """ Provides Zs, logZ """ if 'logZ' in self.__dict__: return self.computeLogV1() L = self.traj.L self.logZs = [dot(self.traj.features_np[0], self.theta)] assert not np.isnan(self.logZs[0]).any(), \ (self.traj.features_np[0], self.theta) for l in range(1, L): N_l = self.traj.num_choices[l] l_vec = MINUS_INF * np.ones((N_l)) conns_back = self.traj.connections_backward[l] w = dot(self.traj.features_np[l], self.theta) assert not np.isnan(w).any() for i in range(N_l): if i in conns_back: l_vec[i] = w[i] + lse([self.logZs[l-1][j] for j in conns_back[i]]) assert not np.isnan(l_vec[i]).any() assert not np.isnan(l_vec).any() self.logZs.append(l_vec) assert(len(self.logZs) == L) self.logZ = lse(self.logZs[L-1]) assert not np.isnan(self.logZ).any() # Make sure we do not overflow. if self.logZ < 100 and self.logZ > -100: self.Z = exp(self.logZ)
def computeValue(self): """ Calls LogZ. Provides logValue """ if 'logValue' in self.__dict__: return self.computeSStats() self.computeLogV1() self.computeLogZ() L = self.traj.L self.logValues = [dot(self.traj.features_np[0], self.theta) - dot(self.sstats[0], self.theta)] assert not np.isnan(self.logValues[0]).any(), \ (self.traj.features_np[0], self.theta) assert lse(self.logValues[0]) >= 0 for l in range(1, L): N_l = self.traj.num_choices[l] l_vec = MINUS_INF * np.ones((N_l)) conns_back = self.traj.connections_backward[l] w = dot(self.traj.features_np[l], self.theta) - dot(self.sstats[l], self.theta) assert not np.isnan(w).any() for i in range(N_l): if i in conns_back: l_vec[i] = w[i] + lse([self.logValues[l-1][j] \ for j in conns_back[i]]) assert not np.isnan(l_vec[i]).any() self.logValues.append(l_vec) # assert(lse(l_vec) >= -1e-2) assert not np.isnan(l_vec).any() assert(len(self.logValues) == L) self.logValue = -lse(self.logValues[L-1]) assert not np.isnan(self.logZ).any()
def computeLogZ(self): """ Reference computation of Z and logZ, in the log domain. Very inefficient. Provides: - logZ Debug info: - Z """ if 'logZ' in self.__dict__: return L = self.traj.L # Weighting statistics. self.Z = 0 self.logZ = MINUS_INF # List of the log weights of all the possible trajectories. intermediate_log_vals = [] def inner(indices): """ Inner working function. """ L_ = len(indices) if L_ == L: v1 = np.zeros_like(self.theta) for l in range(L): v1 += self.traj.features_np[l][indices[l]] intermediate_log_vals.append(dot(v1, self.theta)) else: i = indices[-1] for j in self.traj.connections_forward[L_-1][i]: inner(indices + [j]) for i in range(self.traj.num_choices[0]): inner([i]) self.logZ = lse(intermediate_log_vals) # Make sure we do not overflow. if self.logZ < 100 and self.logZ > -100: self.Z = exp(self.logZ)
def computeProbs(self): """ Compute probabilities. """ if 'probabilities' in self.__dict__: return inf = float('inf') # Compute the weights self.log_ws = [] for l in range(self.traj.L): self.log_ws.append(dot(self.traj.features_np[l], self.theta)) # Forward pass # Init: self.log_forward = [np.zeros(N_l) for N_l in self.traj.num_choices] self.log_forward[0] = self.log_ws[0] - lse(self.log_ws[0]) for l in range(1, self.traj.L): N_l = self.traj.num_choices[l] qs = -inf * np.ones(N_l) for i in range(N_l): if i in self.traj.connections_backward[l]: log_s = lse([self.log_forward[l-1][j] \ for j in self.traj.connections_backward[l][i]]) qs[i] = self.log_ws[l][i] + log_s self.log_forward[l] = qs - lse(qs) # Backward pass self.log_backward = [np.zeros(N_l, dtype=np.float64) \ for N_l in self.traj.num_choices] # Backward pass # A bit more complicated because we need to do it again for every chunk. for l in range(self.traj.L): self.log_backward[l] = self.computeLogBackward(l, min(l+self.k, \ self.traj.L-1)) # Product: self.probabilities = [] self.log_probabilities = [] for l in range(self.traj.L): log_ps = self.log_forward[l] + self.log_backward[l] log_ps -= max(log_ps) # Aggressively clamp the values that are too low to prevent an underflow log_ps[log_ps < -200] = -200 self.log_probabilities.append(log_ps - lse(log_ps)) ps = exp(log_ps) assert sum(ps) > 0, (l, log_ps, self.log_forward[l], self.log_backward[l]) ps /= sum(ps) self.probabilities.append(ps)
def computeLogBackward(self, t, t_start): """ Computes backward recursion for index t, \ starting at index t_start >= t. Does not modify the function. Returns the vector of backward values. """ assert t_start >= t assert self.traj.L > t_start inf = float('inf') # Intermediate vectors vs = [] # Initizaliation N_t_start = self.traj.num_choices[t_start] vs.append(np.zeros(N_t_start, dtype=np.float64)) for l in range(t, t_start)[::-1]: N_l = self.traj.num_choices[l] qs = -inf * np.ones(N_l) for i in range(N_l): if i in self.traj.connections_forward[l]: log_s = lse([vs[-1][j] \ for j in self.traj.connections_forward[l][i]]) qs[i] = self.log_ws[l][i] + log_s vs.append(qs - lse(qs)) return vs[-1]
def test_1(): """ test_1 """ v = [1, 2, 0.0] lv = [log_safe(x) for x in v] assert abs(log(3) - lse(lv)) < 1e-6, (v, lv)