def test_dense_1kx1k_int_hard(dense_1kx1k_int_hard): cost, opt = dense_1kx1k_int_hard ret = lapmod(*sparse_from_dense(cost)) assert len(ret) == 3 assert ret[0] == opt lapjv_ret = lapjv(cost) assert ret[0] == lapjv_ret[0]
def test_dense_100x100_int(dense_100x100_int): cost, opt = dense_100x100_int ret = lapmod(*sparse_from_dense(cost)) assert len(ret) == 3 assert ret[0] == opt lapjv_ret = lapjv(cost) assert ret[0] == lapjv_ret[0]
def test_lapjv_noextension(): cost = get_dense_8x8_int()[0] c = np.r_[cost[:2, :4], [[1001, 1001, 1001, 2001], [2001, 1001, 1001, 1001]]] ret = lapjv(c, extend_cost=False) assert ret[0] - 2002 == 3.0 assert np.all(ret[1] == [1, 2, 0, 3]) assert np.all(ret[2] == [2, 0, 1, 3])
def test_sparse_square(cost, expected): ret = lapjv(cost) assert len(ret) == len(expected) assert cost[range(cost.shape[0]), ret[1]].sum() == ret[0] assert cost[ret[2], range(cost.shape[1])].sum() == ret[0] assert ret[0] == expected[0] assert np.all(ret[1] == expected[1]) assert np.all(ret[2] == expected[2])
def test_sparse_4kx4k_int(sparse_4kx4k_int): cost, mask, opt = sparse_4kx4k_int ret = lapmod(*sparse_from_masked(cost, mask)) assert len(ret) == 3 assert ret[0] == opt cost[~mask] = get_platform_maxint() lapjv_ret = lapjv(cost) assert ret[0] == lapjv_ret[0]
def test_inf_row(): cost = np.array([[0., 0., 0., 0., np.inf], [np.inf, np.inf, 0., 0., 0.], [np.inf, np.inf, np.inf, np.inf, np.inf], [np.inf, np.inf, np.inf, 0., 0.], [0., 0., 0., np.inf, np.inf]], dtype=float) ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == np.inf
def _project(self, matrix): if self.is_scalar(): return 1 else: indexes = lap.lapjv(np.asarray(-matrix)) result = np.zeros(self.size) for row, column in enumerate(indexes[1]): result[row, column] = 1 return result
def test_infs_unsolvable(): cost = np.array([[0., 0., 0., np.inf, np.inf], [np.inf, np.inf, np.inf, 0., 0.], [np.inf, np.inf, np.inf, 0., 0.], [np.inf, np.inf, np.inf, 0., 0.], [0., 0., 0., np.inf, np.inf]], dtype=float) ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == np.inf cost = np.array([[19., 22., 16., np.inf, np.inf], [np.inf, np.inf, np.inf, 4., 13.], [np.inf, np.inf, np.inf, 3., 14.], [np.inf, np.inf, np.inf, 10., 12.], [11., 14., 13., np.inf, np.inf]], dtype=float) ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == np.inf
def test_inf_unique(): cost = np.array([[1000, 4, 1], [1, 1000, 3], [5, 1, 1000]]) cost_ext = np.empty((4, 4)) cost_ext[:] = np.inf cost_ext[:3, :3] = cost cost_ext[3, 3] = 0 ret = lapjv(cost_ext) assert len(ret) == 3 assert ret[0] == 3. assert np.all(ret[1] == [2, 0, 1, 3])
def test_infs_unsolvable(): cost = np.array([[0., 0., 0., np.inf, np.inf], [np.inf, np.inf, np.inf, 0., 0.], [np.inf, np.inf, np.inf, 0., 0.], [np.inf, np.inf, np.inf, 0., 0.], [0., 0., 0., np.inf, np.inf]], dtype=float) lapjv_ret = lapjv(cost) assert lapjv_ret[0] == np.inf ret = lapmod(*sparse_from_masked(cost)) assert len(ret) == 3 assert ret[0] == np.inf cost = np.array([[19., 22., 16., np.inf, np.inf], [np.inf, np.inf, np.inf, 4., 13.], [np.inf, np.inf, np.inf, 3., 14.], [np.inf, np.inf, np.inf, 10., 12.], [11., 14., 13., np.inf, np.inf]], dtype=float) lapjv_ret = lapjv(cost) assert lapjv_ret[0] == np.inf ret = lapmod(*sparse_from_masked(cost)) assert len(ret) == 3 assert ret[0] == np.inf
def _project(self, matrix): if self.is_scalar(): return 1 else: # Note that we use Munkres algorithm, but expand columns from n to m # by replicating each column by group size. mm = np.repeat(matrix, self.col_sum, axis=1) indexes = lap.lapjv(np.asarray(-mm)) result = np.zeros(self.size) reduce = np.repeat(range(len(self.col_sum)), self.col_sum) for row, column in enumerate(indexes[1]): # map expanded column index to reduced group index. result[row, reduce[column]] = 1 return result
def linear_assignment(cost_matrix, thresh): if cost_matrix.size == 0: return np.empty((0, 2), dtype=int), tuple(range(cost_matrix.shape[0])), tuple( range(cost_matrix.shape[1])) matches, unmatched_a, unmatched_b = [], [], [] cost, x, y = lap.lapjv(cost_matrix, extend_cost=True, cost_limit=thresh) for ix, mx in enumerate(x): if mx >= 0: matches.append([ix, mx]) unmatched_a = np.where(x < 0)[0] unmatched_b = np.where(y < 0)[0] matches = np.asarray(matches) return matches, unmatched_a, unmatched_b
def on_epoch_end(self): if self.steps <= 0: return # Skip this on the last epoch. self.steps -= 1 self.match = [] self.unmatch = [] if segment: # Using slow scipy. Make small batches. # Because algorithm is O(n^3), small batches are much faster. # However, this does not find the real optimum, just an approximation. tmp = [] batch = 512 for start in range(0, score.shape[0], batch): end = min(score.shape[0], start + batch) _, x = linear_sum_assignment(self.score[start:end, start:end]) tmp.append(x + start) x = np.concatenate(tmp) else: _, _, x = lapjv(self.score) # Solve the linear assignment problem y = np.arange(len(x), dtype=np.int32) # Compute a derangement for matching whales # self.match :所有同类的两两组合[(ts1, ts2), (ts3, ts4)] for ts in w2ts.values(): d = ts.copy() while True: random.shuffle(d) if not np.any(ts == d): break for ab in zip(ts, d): self.match.append(ab) # Construct unmatched whale pairs from the LAP solution. # self.unmatch :不同类样本的ts两两组合列表[(tsu1, tsu2), (tsu3, tsu4)],x是从匈牙利算法得出的基于score矩阵的结果序列 for i, j in zip(x, y): if i == j: print(self.score) print(x) print(y) print(i, j) assert i != j self.unmatch.append((train[i], train[j])) # Force a different choice for an eventual next epoch. self.score[x, y] = 10000.0 self.score[y, x] = 10000.0 random.shuffle(self.match) random.shuffle(self.unmatch) # print(len(self.match), len(train), len(self.unmatch), len(train)) assert len(self.match) == len(train) and len( self.unmatch) == len(train)
def linear_assignment(cost_matrix, thresh): if cost_matrix.size == 0: return np.empty((0, 2), dtype=np.int64), tuple( range(cost_matrix.shape[0])), tuple(range(cost_matrix.shape[1])) cost, x, y = lap.lapjv(cost_matrix, extend_cost=True, cost_limit=thresh) # return get_matches(x, y) matches, unmatched_a, unmatched_b = [], [], [] for ix, mx in enumerate(x): if mx >= 0: matches.append([ix, mx]) unmatched_a = np.where(x < 0)[0] unmatched_b = np.where(y < 0)[0] matches = np.asarray(matches) # print(f"unmatched_a: {unmatched_a.shape}, unmatched_b: {unmatched_b}. X: {x.shape}, y: {y.shape}, yee: {np.where(y < 0)}") return matches, unmatched_a, unmatched_b
def test_lapjv_arr_loop(): shape = (7, 3) cc = np.array([ 2.593883482138951146e-01, 3.080381437461217620e-01, 1.976243020727339317e-01, 2.462740976049606068e-01, 4.203993396282833528e-01, 4.286184525458427985e-01, 1.706431415909629434e-01, 2.192929371231896185e-01, 2.117769622802734286e-01, 2.604267578125001315e-01]) ii = np.array([0, 0, 1, 1, 2, 2, 5, 5, 6, 6]) jj = np.array([0, 1, 0, 1, 1, 2, 0, 1, 0, 1]) cost = np.empty(shape) cost[:] = 1000. cost[ii, jj] = cc opt, ind1, ind0 = lapjv(cost, extend_cost=True, return_cost=True) assert opt == approx(0.8455356917416, 1e-10) assert np.all(ind0 == [5, 1, 2]) or np.all(ind0 == [1, 5, 2])
def linear_assignment(self, cost, cost_limit): matches = np.empty((0, 2), dtype=int) mismatch_row = np.arange(cost.shape[0], dtype=int) mismatch_col = np.arange(cost.shape[1], dtype=int) if cost.size == 0: return matches, mismatch_row, mismatch_col matches = [] opt, x, y = lap.lapjv(cost, extend_cost=True, cost_limit=cost_limit) for i, xi in enumerate(x): if xi >= 0: matches.append([i, xi]) matches = np.asarray(matches) mismatch_row = np.where(x < 0)[0] mismatch_col = np.where(y < 0)[0] return matches, mismatch_row, mismatch_col
def linear_assignment(cost_matrix): try: import lap _, x, y = lap.lapjv(cost_matrix, extend_cost=True) return np.array([[y[i], i] for i in x if i >= 0]) # except ImportError: from scipy.optimize import linear_sum_assignment x, y = linear_sum_assignment(cost_matrix) return np.array(list(zip(x, y)))
def lsa_solve_lapjv(costs): """Solves the LSA problem using lap.lapjv().""" from lap import lapjv # The lap.lapjv function supports +inf edges but there are some issues. # https://github.com/gatagat/lap/issues/20 # Therefore, replace nans with large finite cost. finite_costs = add_expensive_edges(costs) row_to_col, _ = lapjv(finite_costs, return_cost=False, extend_cost=True) indices = np.array([np.arange(costs.shape[0]), row_to_col], dtype=int).T # Exclude unmatched rows (in case of unbalanced problem). indices = indices[indices[:, 1] != -1] # pylint: disable=unsubscriptable-object rids, cids = indices[:, 0], indices[:, 1] # Ensure that no missing edges were chosen. rids, cids = _exclude_missing_edges(costs, rids, cids) return rids, cids
def _get_assignments(self, data): """ Get the assignments of the 2D points to the regular grid by solving the linear assignment problem using the Jonker-Volgenant algorithm. Parameters ---------- data: np.ndarray The normalized data. Returns ------- int, array-like, array-like The cost and assigments to grid cells. """ # create grid of size n with linearly spaced coordinates, reshape to list of coordinates (size_x*size_y x2) grid = np.dstack( np.meshgrid(np.linspace(0, 1, self.size_x, endpoint=False), np.linspace(0, 1, self.size_y, endpoint=False))).reshape(-1, 2) # get squared euclidean distances between all pairs of grid points and embeddings cost_matrix = cdist(grid, data, "sqeuclidean") """ calculate the linear assignment problem - find the assignments with minimal cost the algorithm works if we have less embeddings than there are grid cells (generalized LAP) -1 is returned if there is no matching embedding, i.e. no samples match the grid cell """ # Try to use lap if it is available - if not, use scipy's linear_sum_assignment. if importlib.util.find_spec("lap") is not None: res = lapjv(cost_matrix, extend_cost=True) cost, grid_indices, assignments = res[0], res[1], grid[res[2]] else: row_indices, col_indices = linear_sum_assignment(cost_matrix) cost = cost_matrix[row_indices, col_indices].sum() grid_indices = np.full((self.size_x * self.size_y), -1, dtype=np.int32) grid_indices[row_indices] = col_indices assignments = grid[row_indices] return cost, grid_indices, assignments
def _compute_interactor_indices_per_frame(self): # Set cofactor interactor indices all_cofactors = np.arange(self.n_interactor_residues_) print('Computing cofactor interactor indices.') ref_frame_coords = np.copy(self.cofactor_interactor_coords_[0]) self.cofactor_interactor_indices_ = np.zeros( (self.n_frames_, self.n_interactor_residues_)) self.cofactor_interactor_indices_[0] = np.arange( self.n_interactor_residues_) for i_frame in range(self.n_frames_): sys.stdout.write("\r Frame: " + str(i_frame + 1) + '/' + str(self.n_frames_) + ' ') sys.stdout.flush() # Get cofactor coordinates of current frame current_frame_coords = self.cofactor_interactor_coords_[i_frame] # Compute distances to the coordinates in frame 0 if not ( self.use_cartesian_coords_ ): # When using internal coordinates, periodic boundary conditions are taken into account in the coordinates distances = cdist(current_frame_coords, ref_frame_coords) else: distances, _ = self._compute_distances_PBC( current_frame_coords, ref_frame_coords, self.traj_.unitcell_lengths[i_frame]) # Solve the linear sum assignment problem _, node_inds, _ = lap.lapjv(distances) # Get cofactor interactor index in each frame by finding the index of the closest cofactor in the reference frame self.cofactor_interactor_indices_[i_frame, all_cofactors] = node_inds print() # Save data np.save( self.out_directory_ + 'cofactor_interactor_indices_' + self.file_end_name_ + '.npy', self.cofactor_interactor_indices_) print('cofactor interactor coordinates and indices written to files: ') print(self.out_directory_ + 'cofactor_interactor_indices_' + self.file_end_name_ + '.npy') return
def save_tsne_grid(img_collection, X_2d, out_res, out_dim): grid = np.dstack( np.meshgrid(np.linspace(0, 1, out_dim), np.linspace(0, 1, out_dim))).reshape(-1, 2) cost_matrix = cdist(grid, X_2d, "sqeuclidean").astype(np.float32) cost_matrix = cost_matrix * (100000 / cost_matrix.max()) row_asses, col_asses, _ = lapjv(cost_matrix) grid_jv = grid[col_asses] out = np.ones((out_dim * out_res, out_dim * out_res, 3)) for pos, img in zip(grid_jv, img_collection[0:to_plot]): h_range = int(np.floor(pos[0] * (out_dim - 1) * out_res)) w_range = int(np.floor(pos[1] * (out_dim - 1) * out_res)) out[h_range:h_range + out_res, w_range:w_range + out_res] = image.img_to_array(img) im = image.array_to_img(out) im.save(out_dir + out_name, quality=100)
def assign_classes(probs, exps, return_cost=False): # TODO: refactor numpy/torch usage exps = np.array(exps) costs = {} classes = np.zeros(probs.shape[0], dtype=np.int64) for exp in np.unique(exps): subset = exps == exp preds = probs[subset] cost, c, _ = lap.lapjv(1 - preds, extend_cost=True) costs[exp] = cost classes[subset] = c if return_cost: return classes, costs else: return classes
def linear_assignment(cost_matrix, thresh): """ Hungarian algorithm: pair new detections with previous tracklets return matches: [idx of tracked_stracks, idx of detections] u_track: [index of undefined track] u_detection: [index of undefined detection] """ if cost_matrix.size == 0: return np.empty((0, 2), dtype=int), tuple(range(cost_matrix.shape[0])), tuple(range(cost_matrix.shape[1])) matches, unmatched_a, unmatched_b = [], [], [] cost, x, y = lap.lapjv(cost_matrix, extend_cost=True, cost_limit=thresh) for ix, mx in enumerate(x): if mx >= 0: matches.append([ix, mx]) unmatched_a = np.where(x < 0)[0] unmatched_b = np.where(y < 0)[0] matches = np.asarray(matches) return matches, unmatched_a, unmatched_b
def adhoc_order(g, alpha=1.0): """ Attempt to determine an ordering based only upon cross-terms between contigs using graphical techniques. 1. Begin with a contig graph where edges are weighted by contact frequency. 2. The graph is then partitioned into subgraphs using Louvain modularity. 3. Using inverse edge weights, the shortest path of the minimum spanning tree of each subgraph is used to define an order. 4. The subgraph orderings are then concatenated together to define a full ordering of the sample. 5. TODO add optimal leaf ordering is possible. 6. Unconnected contigs are included by order of appearance. :param g: graph to order :param alpha: additive constant used in inverse weighting. :return: an ordering """ sg_list = decompose_graph(g) # calculate inter-subgraph weights for use in ordering w = inter_weight_matrix(g, sg_list, norm=True) # perform LAP, but first convert weight matrix to a "cost" matrix ord_x, ord_y = lap.lapjv(1.0 / (w + alpha), return_cost=False) # reorder the subgraphs, just using row order sg_list = [sg_list[i] for i in ord_x] # now find the order through each subgraph isolates = [] new_order = [] for gi in sg_list: # if there is more than one node if gi.order() > 1: inverse_edge_weights(gi) mst = nx.minimum_spanning_tree(gi) inverse_edge_weights(gi) new_order.extend(edgeiter_to_nodelist(dfs_weighted(mst))) else: isolates.extend(gi.nodes()) return np.array(new_order + isolates)
def _get_assignments(self, data): """ Get the assignments of the 2D points to the regular grid by solving the linear assignment problem using the Jonker-Volgenant algorithm. Parameters ---------- data: np.ndarray The normalized data. Returns ------- int, array-like, array-like The cost and assigments to grid cells. """ # create grid of size n with linearly spaced coordinates, reshape to list of coordinates (size_x*size_y x2) grid = np.dstack(np.meshgrid(np.linspace(0, 1, self.size_x, endpoint=False), np.linspace(0, 1, self.size_y, endpoint=False))).reshape(-1, 2) # get squared euclidean distances between all pairs of grid points and embeddings cost_matrix = cdist(grid, data, "sqeuclidean") """ calculate the linear assignment problem - find the assignments with minimal cost the algorithm works if we have less embeddings than there are grid cells (generalized LAP) -1 is returned if there is no matching embedding, i.e. no samples match the grid cell """ # Try to use lap if it is available - if not, use scipy's linear_sum_assignment. if importlib.util.find_spec("lap") is not None: res = lapjv(cost_matrix, extend_cost=True) cost, grid_indices, assignments = res[0], res[1], grid[res[2]] else: row_indices, col_indices = linear_sum_assignment(cost_matrix) cost = cost_matrix[row_indices, col_indices].sum() grid_indices = np.full((self.size_x * self.size_y), -1, dtype=np.int32) grid_indices[row_indices] = col_indices assignments = grid[row_indices] return cost, grid_indices, assignments
def test(self, X): R = np.zeros((X.size, self.n_hidden_sets+1)) R[:,-1] = 1 for i in range(X.shape[0]): x = X[i] for j in range(self.n_hidden_sets): W = self.Ws[j] K = np.dot(W, x) K[K<0] = 0 cost, x_lap, _ = lapjv(-K, extend_cost=True) cost_norm = cost/x.shape[1] R[i,j] = -cost_norm R = np.dot(R, self.Wc) y_pred = np.exp(R)/np.sum(np.exp(R), axis=1).reshape(-1, 1) return y_pred
def assign_ids(self, ids, det_bboxes, weight_iou_with_det_scores=False, match_iou_thr=0.5): """Assign ids. Args: ids (list[int]): Tracking ids. det_bboxes (Tensor): of shape (N, 5) weight_iou_with_det_scores (bool, optional): Whether using detection scores to weight IOU which is used for matching. Defaults to False. match_iou_thr (float, optional): Matching threshold. Defaults to 0.5. Returns: tuple(int): The assigning ids. """ # get track_bboxes track_bboxes = np.zeros((0, 4)) for id in ids: track_bboxes = np.concatenate( (track_bboxes, self.tracks[id].mean[:4][None]), axis=0) track_bboxes = torch.from_numpy(track_bboxes).to(det_bboxes) track_bboxes = bbox_cxcyah_to_xyxy(track_bboxes) # compute distance ious = bbox_overlaps(track_bboxes, det_bboxes[:, :4]) if weight_iou_with_det_scores: ious *= det_bboxes[:, 4][None] dists = (1 - ious).cpu().numpy() # bipartite match if dists.size > 0: cost, row, col = lap.lapjv( dists, extend_cost=True, cost_limit=1 - match_iou_thr) else: row = np.zeros(len(ids)).astype(np.int32) - 1 col = np.zeros(len(det_bboxes)).astype(np.int32) - 1 return row, col
def visualize_fitted_surface(output, points, grid_size, viz=True, path="data/uvmaps/"): os.makedirs(path, exist_ok=True) nx, ny = (grid_size, grid_size) x = np.linspace(0, 1, nx) y = np.linspace(0, 1, ny) xv, yv = np.meshgrid(x, y) xv = np.expand_dims(xv.transpose().flatten(), 1) yv = np.expand_dims(yv.transpose().flatten(), 1) par = np.concatenate([xv, yv], 1) B = output.shape[0] predicted_points = [] surfaces = [] for index in range(B): uv = output[index] C = np.sum(np.square(np.expand_dims(uv, 1) - np.expand_dims(par, 0)), 2) cost, x, y = lap.lapjv(C) p = points[index] p = p[y] fitted_surface, fitted_points = fit_surface(p, grid_size, grid_size, 2, 2) fitted_points = fitted_points - np.expand_dims( np.mean(fitted_points, 0), 0) colors_gt = np.ones((np.array(points[index]).shape[0], 3)) colors_pred = np.ones((np.array(fitted_points).shape[0], 3)) colors_gt[:, 2] = 0 colors_pred[:, 1] = 0 color = np.concatenate([colors_gt, colors_pred]) p = np.concatenate([np.array(points[index]), np.array(fitted_points)]) pcd = visualize_point_cloud(p, colors=color, viz=viz) open3d.io.write_point_cloud("{}pcd_{}.pcd".format(path, index), pcd) predicted_points.append(fitted_points) surfaces.append(fitted_surface) predicted_points = np.stack(predicted_points, 0) return predicted_points, surfaces
def match(self, group_a, group_b, maxScore=float("inf")): self._clear() if (len(group_a) > 0) and (len(group_b) == 0): self.unmatched_a = group_a elif (len(group_a) == 0) and (len(group_b) > 0): self.unmatched_b = group_b elif (len(group_a) == 0) and (len(group_b) == 0): return self.matchedPair, self.unmatched_a, self.unmatched_b else: # Setup the weight matrix costs = np.zeros((len(group_a), len(group_b))) for i in range(len(group_a)): for j in range(len(group_b)): costs[i, j] = self.scoreFunc(group_a[i], group_b[j]) # Perform the match self.cost, self.assign_a, self.assign_b = lap.lapjv( costs, extend_cost=True) # Extract matched pairs and unmatched a and b for i in range(len(self.assign_a)): if self.assign_a[i] == -1: self.unmatched_a.append(group_a[i]) else: a = group_a[i] b = group_b[self.assign_a[i]] if self.scoreFunc(a, b) < maxScore: self.matchedPair.append( (group_a[i], group_b[self.assign_a[i]])) else: self.unmatched_a.append(a) self.unmatched_b.append(b) if len(group_b) > len(group_a): for i in range(len(self.assign_b)): if self.assign_b[i] == -1: self.unmatched_b.append(group_b[i]) return self.matchedPair, self.unmatched_a, self.unmatched_b
def super_zero_heuristic_policy(self): """Algorithm computing ordering from SUPER with zero heuristic return pi (array): Array containing the position each paper is to be presented ordered by paper index. For example, pi = [2, 1] means paper 1 is presented in position 2, and paper 2 is presented in position 1. """ if not self.special: # Solve linear assignment problem to get ordering to present. w_p = lambda j, k: self.f(self.s[self.i, j], k) * (self.g_p(self.b[ j] + 1) - self.g_p(self.b[j])) w_r = lambda j, k: self.hyper * self.g_r(self.s[self.i, j], k) w = np.array([ w_p(j, self.paper_index) + w_r(j, self.paper_index) for j in range(self.d) ]) pi = lap.lapjv(-w)[1] pi += 1 if self.special: # Rank papers from maximum to minimum for alpha breaking ties uniformly at random. alpha = self.f(self.s[self.i]) * (self.g_p(self.b + 1) - self.g_p( self.b)) + self.hyper * self.g_r(self.s[self.i]) if self.subset: alpha_pairs = np.array(zip( alpha[self.subset_idx], np.random.permutation(self.subset_idx)), dtype=[('alpha', float), ('index', float)]) else: alpha_pairs = np.array(zip( alpha, np.random.permutation(self.paper_index)), dtype=[('alpha', float), ('index', float)]) pi = np.argsort( np.lexsort((alpha_pairs['index'], -alpha_pairs['alpha']))) + 1 return pi
def _family_size_lap(prediction, target_size): new = prediction.copy() families_per_day = group_by_day(prediction) id_groups = sum( [ _create_specified_family_size_groups(fam_ids, family_size, target_size) for fam_ids in families_per_day.values() ], [], ) weights = _create_weights(prediction, id_groups, penalty_memo) _, col, _ = lapjv(weights) for i in range(len(col)): new_day = prediction[id_groups[col[i]][0]] for j in id_groups[i]: new[j] = new_day return new
def match_mnist_train_set(): result = torch.DoubleTensor(len(mtrain), 3) # map each digit with hungarian algorithm for digit in range(10): msubset = subset(mtrain, digit) qsubset = subset(qtrain, digit) print( f"Matching digit={digit} mlen={len(msubset)} qlen={len(qsubset)}") n = len(msubset) mimg = collect_images(msubset) qimg = collect_images(qsubset) dd = squared_jittered_distances(mimg, qimg) cost, x, _ = lap.lapjv(dd.numpy(), extend_cost=True) print(f" cost={cost} len={n} average={cost/n}") for i in range(len(msubset)): mindice = msubset.indices[i] qindice = qsubset.indices[x[i]] c = dd[i, x[i]] result[mindice, 0] = qindice result[mindice, 1] = c result[mindice, 2] = qtrainlbls[qindice, 5] return result
def on_epoch_end(self): if self.steps <= 0: return # Skip this on the last epoch. self.steps -= 1 self.match = [] self.unmatch = [] print("计算unmatch pairs") start0 = time.time() _, _, x = lapjv(self.score) # Solve the linear assignment problem print("计算unmatch pairs结束,花费时间: " + str(time.time() - start0)) y = np.arange(len(x), dtype=np.int32) # Compute a derangement for matching whales for ts in w2ts.values(): d = ts.copy() while True: random.shuffle(d) if not np.any(ts == d): break for ab in zip(ts, d): self.match.append(ab) # Construct unmatched whale pairs from the LAP solution. for i, j in zip(x, y): if i == j: print(self.score) print(x) print(y) print(i, j) assert i != j self.unmatch.append((train[i], train[j])) # Force a different choice for an eventual next epoch. self.score[x, y] = 10000.0 self.score[y, x] = 10000.0 random.shuffle(self.match) random.shuffle(self.unmatch) # print(len(self.match), len(train), len(self.unmatch), len(train)) assert len(self.match) == len(train) and len( self.unmatch) == len(train)
def accuracy(pred_labels, real_labels, num_classes, num_samples): num_correct = np.zeros((num_classes, num_classes)) for c_1 in range(num_classes): for c_2 in range(num_classes): num_correct[c_1, c_2] = int( ((pred_labels == c_1) * (real_labels == c_2)).sum()) _, assignments, _ = lap.lapjv(num_samples - num_correct) reordered_pred_labels = np.zeros(num_samples) for c in range(num_classes): reordered_pred_labels[pred_labels == c] = assignments[c] # accuracy per cluster cluster_accs = [] for c in range(num_classes): cluster_accs.append( np.average(reordered_pred_labels[real_labels == c] == c)) return np.average( reordered_pred_labels == real_labels), cluster_accs, assignments
def get_lap_layout(**kwargs): print(' * creating linear assignment layout') out_path = get_path('layouts', 'assignment', **kwargs) if os.path.exists(out_path) and kwargs['use_cache']: return out_path # load the umap layout umap = np.array(read_json(kwargs['umap'], **kwargs)) umap = (umap + 1) / 2 # scale 0:1 # determine length of each side in square grid side = math.ceil(umap.shape[0]**(1 / 2)) # create square grid 0:1 in each dimension grid_x, grid_y = np.meshgrid(np.linspace(0, 1, side), np.linspace(0, 1, side)) grid = np.dstack((grid_x, grid_y)).reshape(-1, 2) # compute pairwise distance costs cost = cdist(grid, umap, 'sqeuclidean') # increase cost cost = cost * (10000000. / cost.max()) # run the linear assignment min_cost, row_assignments, col_assignments = lap.lapjv(np.copy(cost), extend_cost=True) # use the assignment vals to determine gridified positions of `arr` pos = grid[col_assignments] return write_layout(out_path, pos, **kwargs)
def on_epoch_end(self): if self.steps <= 0: return # Skip this on the last epoch. self.steps -= 1 self.match = [] self.unmatch = [] # print(self.use_lap) x = lapjv(self.score)[-1] if self.use_lap else self.get_unmatched() y = np.arange(len(x), dtype=np.int32) # Compute a derangement for matching whales for ts in w2ts.values(): d = ts.copy() while True: random.shuffle(d) if not np.any(ts == d): break for ab in zip(ts, d): self.match.append(ab) # Construct unmatched whale pairs from the LAP solution. for i, j in zip(x, y): if i == j: print(self.score) print(x) print(y) print(i, j) assert i != j self.unmatch.append((train[i], train[j])) # Force a different choice for an eventual next epoch. if self.use_lap: self.score[x, y] = 10000.0 self.score[y, x] = 10000.0 random.shuffle(self.match) random.shuffle(self.unmatch) # print(len(self.match), len(train), len(self.unmatch), len(train)) assert len(self.match) == len(train) and len( self.unmatch) == len(train)
def match_mnist_train_set(): result = torch.DoubleTensor(len(mtest), 3) # We found out which xnist digits make # the second part of the 10k mnist test set! print(f"Doing hsf0") revtable = torch.LongTensor(40000) revtable[:] = -1 for i in range(len(qtest)): if qtestlbls[i, 5] < 40000: revtable[qtestlbls[i, 5]] = i for mindice in range(5001, 10000): qindice = revtable[35000 + mindice - 5001] assert mtest.targets[mindice] == qtestlbls[qindice, 0] result[mindice, 0] = qindice result[mindice, 1] = 0 result[mindice, 2] = qtestlbls[qindice, 5] # map hsf4 with hungarian algorithm for digit in range(10): msubset = mnist_test_subset(digit, True) qsubset = qmnist_test_subset(digit, True) print( f"Matching digit={digit} mlen={len(msubset)} qlen={len(qsubset)}") n = len(msubset) mimg = collect_images(msubset) qimg = collect_images(qsubset) dd = squared_jittered_distances(mimg, qimg) cost, x, _ = lap.lapjv(dd.numpy(), extend_cost=True) print(f" cost={cost} len={n} average={cost/n}") for i in range(len(msubset)): mindice = msubset.indices[i] qindice = qsubset.indices[x[i]] c = dd[i, x[i]] result[mindice, 0] = qindice result[mindice, 1] = c result[mindice, 2] = qtestlbls[qindice, 5] return result
def run_lap_lapjv(matrix, printlowestcost): #print module name temp = inspect.stack()[0][3] method_name = temp[4:] print(" %s" % (method_name), end=' ') #start timing t_start = time.time() lowest_cost, row_ind, column_ind = lap.lapjv(matrix) t_end = time.time() #end timing #print minimum cost information with varying verbosity if printlowestcost: if args.verbose: lowest_cost = 0.00 for i in range(len(row_ind)): lowest_cost += matrix[i, row_ind[i]] print("%18s %6d %5.3f" % (" ", i, lowest_cost)) print(" %12s %s %5.3f" % (method_name, "minimum cost", lowest_cost)) del row_ind del column_ind return t_end - t_start
def test_dense_100x100_int(dense_100x100_int): cost, opt = dense_100x100_int ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == opt
def run_grid(input_glob, left_image, right_image, left_right_scale, output_path, tsne_dimensions, tsne_perplexity, tsne_learning_rate, width, height, aspect_ratio, drop_to_fit, fill_shade, vectors_file, do_prune, clip_range, subsampling, model, layer, pooling, do_crop, grid_file, use_imagemagick, grid_spacing, show_links, links_max_threshold, min_distance, max_distance, max_group_size, do_reload=False, do_tsne=False, do_reduce_hack=False, do_pca=False): # make output directory if needed if output_path != '' and not os.path.exists(output_path): os.makedirs(output_path) if do_reload: images = read_list(output_path, "image_files.txt", numeric=False) X = read_list(output_path, "image_vectors.txt", numeric=True) print("Reloaded {} images and {} vectors".format(len(images), X.shape)) num_images = len(images) avg_colors = analyze_images_colors(images, 'rgb') else: ## compute width,weight from image list and provided defaults if input_glob is not None: images = get_image_list(input_glob) num_images = len(images) if vectors_file is not None: X = read_json_vectors(vectors_file) else: X = analyze_images(images, model, layer, pooling, do_crop, subsampling) if do_prune: images, X = run_prune(images, X) if clip_range: images, X = run_clip(images, X, clip_range) # images, X = run_kmeans(images, X) # save data write_list(images, output_path, "image_files.txt") write_list(X, output_path, "image_vectors.txt") ## Lookup left/right images left_image_index = None right_image_index = None # scale X by left/right axis if left_image is not None and right_image is not None: left_image_index = index_from_substring(images, left_image) right_image_index = index_from_substring(images, right_image) if left_image_index is not None: # todo: confirm this is how to stretch by a vector lr_vector = X[right_image_index] - X[left_image_index] lr_vector = lr_vector / np.linalg.norm(lr_vector) X_new = np.zeros_like(X) for i in range(len(X)): len_x = np.linalg.norm(X[i]) norm_x = X[i] / len_x scale_factor = 1.0 + left_right_scale * (1.0 + np.dot(norm_x, lr_vector)) new_length = len_x * scale_factor # print("Vector {}: length went from {} to {}".format(i, len_x, new_length)) X_new[i] = new_length * norm_x X = X_new # TODO: filtering here if min_distance is not None and min_distance > 0: reject_dir = os.path.join(output_path, "rejects_min") if reject_dir != '' and not os.path.exists(reject_dir): os.makedirs(reject_dir) images, X = filter_distance_min(images, X, min_distance, reject_dir) if max_distance is not None and max_distance > 0: reject_dir = os.path.join(output_path, "rejects_max") if reject_dir != '' and not os.path.exists(reject_dir): os.makedirs(reject_dir) images, X = filter_distance_max(images, X, max_distance, reject_dir, max_group_size) grid_images, width, height = set_grid_size(images, width, height, aspect_ratio, drop_to_fit) num_grid_images = len(grid_images) print("Compare: {} and {}".format(num_grid_images, width * height)) # this line is a hack for now X = np.asarray(X[:num_grid_images]) print("SO X {}".format(X.shape)) if do_tsne: print("Running tsne on {} images...".format(num_grid_images)) tsne = TSNE(n_components=tsne_dimensions, learning_rate=tsne_learning_rate, perplexity=tsne_perplexity, verbose=2).fit_transform(X) else: print("Running umap on {} images...".format(num_grid_images)) tsne = umap.UMAP().fit_transform(X) print("EMBEDDING SHAPE {}".format(tsne.shape)) avg_colors = analyze_images_colors(grid_images, 'rgb') data = [] for i, f in enumerate(grid_images): point = [((tsne[i, k] - np.min(tsne[:, k])) / (np.max(tsne[:, k]) - np.min(tsne[:, k]))).tolist() for k in range(tsne_dimensions)] data.append({"path": grid_images[i], "point": point}) with open(os.path.join(output_path, "points.json"), 'w') as outfile: json.dump(data, outfile) if left_image_index is not None: data2d = fit_to_unit_square(tsne, 1, 1) else: data2d = fit_to_unit_square(tsne, width, height) plt.figure(figsize=(8, 8)) plt.xlim(-0.1, 1.1) plt.ylim(-0.1, 1.1) plt.gca().invert_yaxis() grays = np.linspace(0, 0.8, len(data2d)) plt.scatter(data2d[:, 0], data2d[:, 1], c=avg_colors, edgecolors='none', marker='o', s=24) if left_image_index is not None: plt.scatter(data2d[left_image_index:left_image_index + 1, 0], data2d[left_image_index:left_image_index + 1, 1], facecolors='none', edgecolors='r', marker='o', s=24 * 3) plt.scatter(data2d[right_image_index:right_image_index + 1, 0], data2d[right_image_index:right_image_index + 1, 1], facecolors='none', edgecolors='g', marker='o', s=24 * 3) plt.savefig(os.path.join(output_path, "embedding.png"), bbox_inches='tight') # this is an experimental section where left/right image can be given if left_image_index is not None: origin = data2d[left_image_index] data2d = data2d - origin dest = data2d[right_image_index] x_axis = np.array([1, 0]) theta = np.arctan2(dest[1], dest[0]) print("Spin angle is {}".format(np.rad2deg(theta))) # theta = np.deg2rad(90) # print("Spin angle is {}".format(np.rad2deg(theta))) # # http://scipython.com/book/chapter-6-numpy/examples/creating-a-rotation-matrix-in-numpy/ a_c, a_s = np.cos(theta), np.sin(theta) R = np.matrix([[a_c, -a_s], [a_s, a_c]]) data2d = np.array(data2d * R) # print("IS: ", data2d.shape) data2d = fit_to_unit_square(data2d, width, height) # TODO: this is a nasty cut-n-paste of above with different filename plt.figure(figsize=(8, 8)) plt.xlim(-0.1, 1.1) plt.ylim(-0.1, 1.1) plt.gca().invert_yaxis() plt.scatter(data2d[:, 0], data2d[:, 1], c=avg_colors, edgecolors='none', marker='o', s=24) if left_image_index is not None: plt.scatter(data2d[left_image_index:left_image_index + 1, 0], data2d[left_image_index:left_image_index + 1, 1], facecolors='none', edgecolors='r', marker='o', s=48) plt.scatter(data2d[right_image_index:right_image_index + 1, 0], data2d[right_image_index:right_image_index + 1, 1], facecolors='none', edgecolors='g', marker='o', s=48) plt.savefig(os.path.join(output_path, "embedding_spun.png"), bbox_inches='tight') write_list(data2d, output_path, "embedding_coords.txt") # TSNE is done, setup layout for grid assignment max_width, max_height = 1, 1 if (width > height): max_height = height / width elif (width < height): max_width = width / height xv, yv = np.meshgrid(np.linspace(0, max_width, width), np.linspace(0, max_height, height)) grid = np.dstack((xv, yv)).reshape(-1, 2) # this strange step removes corners grid, indexed_lookup = reduce_grid_targets(grid, num_grid_images, do_reduce_hack) # print("G", grid.shape, grid[0]) # print("D2D", data2d.shape) cost = distance.cdist(grid, data2d, 'euclidean') # cost = distance.cdist(grid, data2d, 'sqeuclidean') cost = cost * (100000. / cost.max()) # print("C", cost.shape, cost[0][0]) if using_python_lap: print("Starting assignment (this can take a few minutes)") min_cost2, row_assigns2, col_assigns2 = lap.lapjv( cost, extend_cost=do_reduce_hack) print("Assignment complete") else: # note slightly different API row_assigns2, col_assigns2, min_cost2 = lapjv.lapjv( cost, verbose=True, force_doubles=False) grid_jv2 = grid[col_assigns2] # print(col_assigns2.shape) plt.figure(figsize=(8, 8)) plt.xlim(-0.1, 1.1) plt.ylim(-0.1, 1.1) plt.gca().invert_yaxis() for start, end, c in zip(data2d, grid_jv2, avg_colors): plt.arrow(start[0], start[1], end[0] - start[0], end[1] - start[1], color=c, head_length=0.01, head_width=0.01) if left_image_index is not None: plt.scatter(data2d[left_image_index:left_image_index + 1, 0], data2d[left_image_index:left_image_index + 1, 1], facecolors='none', edgecolors='r', marker='o', s=48) plt.scatter(data2d[right_image_index:right_image_index + 1, 0], data2d[right_image_index:right_image_index + 1, 1], facecolors='none', edgecolors='g', marker='o', s=48) plt.savefig(os.path.join(output_path, 'movement.png'), bbox_inches='tight') num_grid_spaces = len(indexed_lookup) num_actual_images = len(row_assigns2) num_missing = num_grid_spaces - num_actual_images using_placeholder = False if num_missing > 0: # makde a note that placeholder is in use using_placeholder = True # add a blank entry to the vectors _, v_len = X.shape X2 = np.append(X, [np.zeros(v_len)], axis=0) print("Updating vectors from {} to {}".format(X.shape, X2.shape)) X = X2 # add blank entry to images # sniff the aspect ratio of the first file with Image.open(grid_images[0]) as img: im_width = img.size[0] im_height = img.size[1] im_array = np.full([im_height, im_width, 3], [fill_shade, fill_shade, fill_shade]).astype( np.uint8) # im_array = np.zeros([im_width, im_height, 3]).astype(np.uint8) blank_img = Image.fromarray(im_array) blank_image_path = os.path.join(output_path, "blank.png") blank_img.save(blank_image_path) blank_index = len(grid_images) grid_images.append(blank_image_path) # now grow row assignments, giving all remaining to new blanks residuals = np.full([num_missing], blank_index) row_assigns2 = np.append(row_assigns2, residuals) reverse_lookup = np.zeros(num_grid_spaces, dtype=int) reverse_lookup[indexed_lookup] = np.arange(num_grid_spaces) image_indexes = row_assigns2[reverse_lookup] img_grid_vectors = X[image_indexes] g_len, g_dim = img_grid_vectors.shape img_grid_shaped = img_grid_vectors.reshape(height, width, g_dim) with open(os.path.join(output_path, "grid_vectors.json"), 'w') as outfile: json.dump(img_grid_shaped.tolist(), outfile) n_images = np.asarray(grid_images) image_grid = n_images[image_indexes] montage_filelist = write_list(image_grid, output_path, "montage_{}x{}.txt".format(width, height), quote=True) grid_file_path = os.path.join(output_path, grid_file) grid_im_file_path = os.path.join(output_path, "{}".format(grid_file)) left_right_path = os.path.join(output_path, "left_right.jpg") if use_imagemagick: command = "montage @{} -geometry +0+0 -tile {}x{} {}".format( montage_filelist, width, height, grid_im_file_path) # print("running imagemagick montage: {}".format(command)) os.system(command) # if left_image_index is not None: # command = "montage '{}' '{}' -geometry +0+0 -tile 2x1 {}".format( # images[left_image_index], images[right_image_index], left_right_path) # os.system(command) else: # image vectors are in X links = None if show_links: links = [] img_grid_vectors = X[image_indexes] for r in range(height): row = [] links.append(row) for c in range(width): idx = r * width + c cur_v = img_grid_vectors[idx] if c < width - 1: left_v = img_grid_vectors[idx + 1] if using_placeholder and (not cur_v.any() or not left_v.any()): dist_left = -1 else: dist_left = np.linalg.norm(cur_v - left_v) else: dist_left = -1 if r < height - 1: down_v = img_grid_vectors[idx + width] if using_placeholder and (not cur_v.any() or not down_v.any()): dist_down = -1 else: dist_down = np.linalg.norm(cur_v - down_v) else: dist_down = -1 cell = [dist_left, dist_down] row.append(cell) links = np.array(links) # normalize to 0-1 if links_max_threshold is not None: num_removed = (links > links_max_threshold).sum() links[links > links_max_threshold] = -1 num_left = (links > 0).sum() print("removed {} links, {} left".format( num_removed, num_left)) links_max = np.amax(links) valid_vals = np.where(links > 0) links_min = np.amin(links[valid_vals]) print("Normalizing to {}/{}".format(links_min, links_max)) links = ((links - links_min) / (links_max - links_min)) print("Links is {}".format(links.shape)) img = make_grid_image(image_grid, width, height, grid_spacing, links) img.save(grid_file_path) if left_image_index is not None: img = make_grid_image([ grid_images[left_image_index], grid_images[right_image_index] ], 2, 1, 1) img.save(left_right_path)
def test_lapjv_non_square_fail(): with raises(ValueError): lapjv(np.zeros((3, 2)))
def test_all_inf(): cost = np.empty((5, 5), dtype=float) cost[:] = np.inf ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == np.inf
parser.add_argument('--density', type=float, default=1.0) parser.add_argument('--seed', type=int, default=123) parser.add_argument('--sparse', action="store_true") return parser.parse_args() if __name__ == "__main__": args = parse_args() if args.seed > 0: np.random.seed(args.seed) X = sparse.random(args.dim, args.dim, args.density, dtype=np.float32) X = X.tocsr() X.data *= args.max_value t = time() Xd = np.asarray(X.todense()) _, src_ass, _ = lapjv((Xd.max() - Xd)) jv_time = int(1000 * (time() - t)) jv_score = X[(np.arange(X.shape[0]), src_ass)].sum() print({ "jv_time": jv_time, "jv_score": jv_score, }, file=sys.stderr) # print('\n'.join(np.hstack(X).astype('str'))) X_coo = X.tocoo() df = pd.DataFrame({"row": X_coo.row, "col": X_coo.col, "data": X_coo.data}) df.to_csv('./graph', header=None, sep=' ', index=False)
def test_lapjv_cost_limit(): cost = get_dense_8x8_int()[0] ret = lapjv(cost[:3, :3], cost_limit=4.99) assert ret[0] == 3.0 assert np.all(ret[1] == [1, 2, -1]) assert np.all(ret[2] == [-1, 0, 1])
def test_lapjv_extension(): cost = get_dense_8x8_int()[0] ret = lapjv(cost[:2, :4], extend_cost=True) assert ret[0] == 3.0 assert np.all(ret[1] == [1, 2]) assert np.all(ret[2] == [-1, 0, 1, -1])
def test_lapjv_empty(): with raises(ValueError): lapjv(np.ndarray([]))
def train(self, X, y): R = np.zeros((X.size, self.n_hidden_sets+1)) R[:,-1] = 1 Ds = list() for i in range(X.shape[0]): Ds.append(list()) x = X[i] for j in range(self.n_hidden_sets): W = self.Ws[j] K = np.dot(W, x) K[K<0] = 0 cost, x_lap, _ = lapjv(-K, extend_cost=True) D = np.zeros((self.n_elements, x.shape[1])) for k in range(self.n_elements): if x_lap[k] != -1: D[k, x_lap[k]] = 1 Ds[i].append(D) cost_norm = cost/x.shape[1] R[i,j] = -cost_norm S = np.dot(R, self.Wc) y_pred = np.exp(S)/np.sum(np.exp(S), axis=1).reshape(-1, 1) E = y - y_pred ## Backprop upd_Ws = np.zeros((self.n_hidden_sets, self.n_elements, self.d)) upd_Wc = np.zeros((self.n_hidden_sets+1, self.n_classes)) for i in range(X.shape[0]): x = X[i] for j in range(self.n_hidden_sets): upd_Ws[j] = upd_Ws[j] + np.dot(Ds[i][j], x.T)*np.dot(E[i,:], self.Wc[j,:]) upd_Wc += np.outer(R[i,:].T, E[i,:]) self.t += 1 for j in range(self.n_hidden_sets): g_t = upd_Ws[j]*1./x.shape[1] self.m_t[j] = self.beta_1*self.m_t[j] + (1-self.beta_1)*g_t self.v_t[j] = self.beta_2*self.v_t[j] + (1-self.beta_2)*(np.square(g_t)) m_cap = self.m_t[j]/(1-(self.beta_1**self.t)) v_cap = self.v_t[j]/(1-(self.beta_2**self.t)) self.Ws[j] = self.Ws[j] + (self.lr*m_cap)/(np.sqrt(v_cap)+self.epsilon) g_t = upd_Wc*1./x.shape[1] self.m_t_c = self.beta_1*self.m_t_c + (1-self.beta_1)*g_t self.v_t_c = self.beta_2*self.v_t_c + (1-self.beta_2)*np.square(g_t) m_cap= self.m_t_c/(1-(self.beta_1**self.t)) v_cap = self.v_t_c/(1-(self.beta_2**self.t)) self.Wc = self.Wc + (self.lr*m_cap)/(np.sqrt(v_cap)+self.epsilon) return y_pred
def test_lapjv_non_contigous(): cost = get_dense_8x8_int()[0] ret = lapjv(cost[:3, :3]) assert ret[0] == 8.0 assert np.all(ret[1] == [1, 2, 0]) assert np.all(ret[2] == [2, 0, 1])
def test_eps(dense_eps): cost, opt = dense_eps ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == opt
def get_data(seed): cost, mask = get_sparse_int(5000, 1000, 0.01, hard=False, seed=seed) cost_ = cost.copy() cost_[~mask] = get_platform_maxint() opt = lapjv(cost_)[0] return cost, mask, opt
def test_sparse_4kx4k_int(sparse_4kx4k_int): cost, mask, opt = sparse_4kx4k_int cost[~mask] = get_platform_maxint() ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == opt
def test_dense_1kx1k_int_hard(dense_1kx1k_int_hard): cost, opt = dense_1kx1k_int_hard ret = lapjv(cost) assert len(ret) == 3 assert ret[0] == opt
def get_hard_data(sz, rng, seed): cost = get_dense_int(sz, 100, hard=True, seed=seed) opt = lapjv(cost)[0] return cost, opt