Source code for crop_row_connector.Hungarian_algorithm
"""Implementation of the hungarian method."""
from typing import Any
import numpy as np
def _min_zero_row(zero_mat: np.ndarray, mark_zero: list[tuple[int, Any]]) -> None:
# Find the row
min_row = [99999, -1]
for row_num in range(zero_mat.shape[0]):
if np.sum(zero_mat[row_num]) > 0 and min_row[0] > np.sum(zero_mat[row_num]):
min_row = [np.sum(zero_mat[row_num]), row_num]
# Marked the specific row and column as False
zero_index = np.where(zero_mat[min_row[1]])[0][0]
mark_zero.append((min_row[1], zero_index))
zero_mat[min_row[1], :] = False
zero_mat[:, zero_index] = False
def _mark_matrix(mat: np.ndarray) -> tuple[list[tuple[int, Any]], list[int], list[int]]:
# Transform the matrix to boolean matrix(0 = True, others = False)
cur_mat = mat
zero_bool_mat = cur_mat == 0
zero_bool_mat_copy = zero_bool_mat.copy()
# Recording possible answer positions by marked_zero
marked_zero: list[tuple[int, Any]] = []
while True in zero_bool_mat_copy:
_min_zero_row(zero_bool_mat_copy, marked_zero)
# ic(zero_bool_mat_copy)
# ic(marked_zero)
# ic(len(marked_zero))
# Recording the row and column positions separately.
marked_zero_row = []
marked_zero_col = []
for i in range(len(marked_zero)):
marked_zero_row.append(marked_zero[i][0])
marked_zero_col.append(marked_zero[i][1])
# Step 2-2-1
non_marked_row = list(set(range(cur_mat.shape[0])) - set(marked_zero_row))
marked_cols = []
check_switch = True
while check_switch:
check_switch = False
for i in range(len(non_marked_row)):
row_array = zero_bool_mat[non_marked_row[i], :]
for j in range(row_array.shape[0]):
# Step 2-2-2
if row_array[j] and j not in marked_cols:
# Step 2-2-3
marked_cols.append(j)
check_switch = True
for row_num, col_num in marked_zero:
# Step 2-2-4
if row_num not in non_marked_row and col_num in marked_cols:
# Step 2-2-5
non_marked_row.append(row_num)
check_switch = True
# Step 2-2-6
marked_rows = list(set(range(mat.shape[0])) - set(non_marked_row))
return (marked_zero, marked_rows, marked_cols)
def _adjust_matrix(mat: np.ndarray, cover_rows: list[int], cover_cols: list[int]) -> np.ndarray:
cur_mat = mat
non_zero_element = []
# Step 4-1
for row in range(len(cur_mat)):
if row not in cover_rows:
for i in range(len(cur_mat[row])):
if i not in cover_cols:
non_zero_element.append(cur_mat[row][i])
min_num = min(non_zero_element)
# Step 4-2
for row in range(len(cur_mat)):
if row not in cover_rows:
for i in range(len(cur_mat[row])):
if i not in cover_cols:
cur_mat[row, i] = cur_mat[row, i] - min_num
# Step 4-3
for row in range(len(cover_rows)):
for col in range(len(cover_cols)):
cur_mat[cover_rows[row], cover_cols[col]] = cur_mat[cover_rows[row], cover_cols[col]] + min_num
return cur_mat
[docs]
def hungarian_algorithm(mat: np.ndarray) -> np.ndarray:
"""Hungarian Algorithm:
Finding the minimum value in linear assignment problem.
Therefore, we can find the minimum value set in net matrix
by using Hungarian Algorithm. In other words, the maximum value
and elements set in cost matrix are available.
"""
dim = mat.shape[0]
cur_mat = mat
# Step 1 - Every column and every row subtract its internal minimum
for row_num in range(mat.shape[0]):
cur_mat[row_num] = cur_mat[row_num] - np.min(cur_mat[row_num])
for col_num in range(mat.shape[1]):
cur_mat[:, col_num] = cur_mat[:, col_num] - np.min(cur_mat[:, col_num])
zero_count = 0
ans_pos = np.array([])
while zero_count < dim:
# Step 2 & 3
ans_pos, marked_rows, marked_cols = _mark_matrix(cur_mat)
zero_count = len(marked_rows) + len(marked_cols)
if zero_count < dim:
cur_mat = _adjust_matrix(cur_mat, marked_rows, marked_cols)
return np.array(ans_pos)