thearn commited on
Commit
52e1813
·
1 Parent(s): bf77b60

updated for py3

Browse files
Files changed (3) hide show
  1. .gitignore +122 -0
  2. algorithms.py +142 -41
  3. run.py +15 -7
.gitignore ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a script, but they may be committed to
32
+ # repositories by accident.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # PEP 582; __pypackages__ directory
88
+ __pypackages__/
89
+
90
+ # Celery stuff
91
+ celerybeat-schedule
92
+ celerybeat.pid
93
+
94
+ # SageMath parsed files
95
+ *.sage.py
96
+
97
+ # Environments
98
+ .env
99
+ .venv
100
+ env/
101
+ venv/
102
+ ENV/
103
+ env.bak/
104
+ venv.bak/
105
+
106
+ # Spyder project settings
107
+ .spyderproject
108
+ .spyproject
109
+
110
+ # Rope project settings
111
+ .ropeproject
112
+
113
+ # mkdocs documentation
114
+ /site
115
+
116
+ # mypy
117
+ .mypy_cache/
118
+ .dmypy.json
119
+ dmypy.json
120
+
121
+ # Pyre type checker
122
+ .pyre/
algorithms.py CHANGED
@@ -2,9 +2,14 @@ from scipy.signal import fftconvolve as conv
2
  import numpy as np
3
  import itertools
4
  import time
 
5
 
 
 
 
 
6
 
7
- def local_search(A, loc):
8
  """
9
  Utility function to verify local optimality of a
10
  subarray slice specification 'loc' of array 'A'
@@ -15,64 +20,160 @@ def local_search(A, loc):
15
  Needed due to indeterminacy of precise indices corresponding
16
  to maximization of the convolution operation
17
  """
18
- r1, r2, c1, c2 = loc[0].start, loc[0].stop, loc[1].start, loc[1].stop
19
- mx = A[loc].sum()
 
 
 
 
 
 
20
  for i, j, k, l in itertools.product([-1, 0, 1], repeat=4):
21
- loc_ = (slice(r1 + i, r2 + j), slice(c1 + k, c2 + l))
22
- val = A[loc_].sum()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  if val >= mx:
24
  mx = val
25
  loc2 = loc_
26
  return loc2, mx
27
 
28
-
29
- def brute_submatrix_max(A):
30
  """
31
  Searches for the rectangular subarray of A with maximum sum
32
  Uses brute force searching
33
  """
34
  M, N = A.shape
35
- t0 = time.time()
36
- this_location, max_value = ((0, 0), (0, 0)), 0
37
- for m, n in itertools.product(xrange(M), xrange(N)):
38
- for i, k in itertools.product(xrange(M - m + 1), xrange(N - n + 1)):
39
- this_location = (slice(i, i + m), slice(k, k + n))
40
- value = A[this_location].sum()
41
- if value >= max_value:
42
- max_value = value
43
- location = this_location
44
- t = time.time() - t0
45
- return location, max_value, t
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- def fft_submatrix_max(A):
49
  """
50
  Searches for the rectangular subarray of A with maximum sum
51
  Uses FFT-based convolution operations
52
  """
53
  M, N = A.shape
54
- this_location, max_value = ((0, 0), (0, 0)), 0
55
- t0 = time.time()
56
- for m, n in itertools.product(xrange(2, M), xrange(2, N)):
57
- convolved = conv(A, np.ones((m, n)), mode='same')
58
- row, col = np.unravel_index(convolved.argmax(), convolved.shape)
59
- # index offsets for odd dimension length:
60
- if m % 2 == 1:
61
- m_off = 1
62
- else:
63
- m_off = 0
64
- if n % 2 == 1:
65
- n_off = 1
66
- else:
67
- n_off = 0
 
 
68
 
69
- this_location = (
70
- slice(row - m / 2, row + m / 2 + m_off), slice(col - n / 2, col + n / 2 + n_off))
71
- value = A[this_location].sum()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- if value >= max_value:
74
- max_value = value
75
- location = this_location
76
- location, max_value = local_search(A, location)
77
- t = time.time() - t0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  return location, max_value, t
 
2
  import numpy as np
3
  import itertools
4
  import time
5
+ from typing import Tuple, Any, Iterator
6
 
7
+ # Define type aliases for clarity
8
+ SlicePair = Tuple[slice, slice]
9
+ Numeric = Any # Could be int or float, depending on numpy array type
10
+ Location = Tuple[int, int] # For unravel_index output
11
 
12
+ def local_search(A: np.ndarray, loc: SlicePair) -> Tuple[SlicePair, Numeric]:
13
  """
14
  Utility function to verify local optimality of a
15
  subarray slice specification 'loc' of array 'A'
 
20
  Needed due to indeterminacy of precise indices corresponding
21
  to maximization of the convolution operation
22
  """
23
+ r1_start: int = loc[0].start
24
+ r2_stop: int = loc[0].stop
25
+ c1_start: int = loc[1].start
26
+ c2_stop: int = loc[1].stop
27
+
28
+ mx: Numeric = A[loc].sum()
29
+ loc2: SlicePair = loc
30
+
31
  for i, j, k, l in itertools.product([-1, 0, 1], repeat=4):
32
+ # Ensure slices do not go out of bounds, though Python handles negative/large slice indices gracefully
33
+ # For robustness, explicit checks could be added here if strict boundary adherence is needed
34
+ # However, standard slice behavior might be sufficient for this algorithm's purpose
35
+ current_r1 = r1_start + i
36
+ current_r2 = r2_stop + j
37
+ current_c1 = c1_start + k
38
+ current_c2 = c2_stop + l
39
+
40
+ # Ensure slice order is maintained (start <= stop)
41
+ if current_r1 > current_r2 or current_c1 > current_c2:
42
+ continue
43
+
44
+ loc_: SlicePair = (slice(current_r1, current_r2), slice(current_c1, current_c2))
45
+
46
+ # Handle empty slices that can result from perturbations
47
+ if loc_[0].start == loc_[0].stop or loc_[1].start == loc_[1].stop:
48
+ val: Numeric = 0 # or handle as appropriate, e.g., continue
49
+ else:
50
+ val: Numeric = A[loc_].sum()
51
+
52
  if val >= mx:
53
  mx = val
54
  loc2 = loc_
55
  return loc2, mx
56
 
57
+ def brute_submatrix_max(A: np.ndarray) -> Tuple[SlicePair, Numeric, float]:
 
58
  """
59
  Searches for the rectangular subarray of A with maximum sum
60
  Uses brute force searching
61
  """
62
  M, N = A.shape
63
+ t0: float = time.time()
64
+ location: SlicePair = (slice(0, 0), slice(0, 0)) # Default to an empty slice
65
+ max_value: Numeric = -np.inf # Initialize with a very small number or A.min() if appropriate
 
 
 
 
 
 
 
 
66
 
67
+ # Ensure there's at least one element to avoid issues with empty A
68
+ if M == 0 or N == 0:
69
+ return location, 0, time.time() - t0
70
+
71
+ max_value = A[slice(0,1), slice(0,1)].sum() # Initialize with the first element's sum
72
+
73
+ for m in range(1, M + 1): # Iterate over possible submatrix heights
74
+ for n in range(1, N + 1): # Iterate over possible submatrix widths
75
+ for r_start in range(M - m + 1): # Iterate over possible starting rows
76
+ for c_start in range(N - n + 1): # Iterate over possible starting columns
77
+ this_location: SlicePair = (slice(r_start, r_start + m), slice(c_start, c_start + n))
78
+ value: Numeric = A[this_location].sum()
79
+ if value >= max_value:
80
+ max_value = value
81
+ location = this_location
82
+ t: float = time.time() - t0
83
+ return location, max_value, t
84
 
85
+ def fft_submatrix_max(A: np.ndarray) -> Tuple[SlicePair, Numeric, float]:
86
  """
87
  Searches for the rectangular subarray of A with maximum sum
88
  Uses FFT-based convolution operations
89
  """
90
  M, N = A.shape
91
+ location: SlicePair = (slice(0,0), slice(0,0)) # Default for empty or small arrays
92
+ max_value: Numeric = -np.inf
93
+ t0: float = time.time()
94
+
95
+ if M < 2 or N < 2: # Convolution requires at least 2x2 for this setup
96
+ # Fallback to brute force or handle as an edge case
97
+ # For simplicity, returning default if too small for meaningful FFT
98
+ # Or, could call brute_submatrix_max for small arrays
99
+ if M > 0 and N > 0:
100
+ return brute_submatrix_max(A) # Or a simpler handler
101
+ return location, 0, time.time() - t0
102
+
103
+
104
+ max_value = A[slice(0,1), slice(0,1)].sum() # Initialize with the first element's sum
105
+ location = (slice(0,1), slice(0,1))
106
+
107
 
108
+ for m in range(1, M + 1): # Iterate from 1x1 up to MxN submatrices
109
+ for n in range(1, N + 1):
110
+ # Kernel for convolution
111
+ kernel = np.ones((m, n))
112
+
113
+ # Perform convolution
114
+ # 'valid' mode ensures convolved output corresponds to sums of m x n submatrices
115
+ # 'same' mode pads, which might be what the original code intended with manual index adjustment
116
+ # Using 'valid' simplifies index mapping if the goal is direct submatrix sums
117
+ # If 'same' is kept, careful index adjustment is needed as in original
118
+
119
+ # Reverting to 'same' to match original logic more closely, then adjusting indices
120
+ convolved: np.ndarray = conv(A, kernel, mode='same')
121
+
122
+ # Find the maximum value in the convolved array
123
+ flat_idx: np.intp = convolved.argmax()
124
+ row_center, col_center = np.unravel_index(flat_idx, convolved.shape) # type: ignore
125
 
126
+ # Calculate slice indices based on center from 'same' convolution
127
+ # For 'same' mode, the peak corresponds to the top-left of the kernel aligned with that point
128
+ # The original code's index calculation seems to assume the peak is the center of the submatrix
129
+
130
+ # Adjusting for 'same' mode where peak is top-left of kernel placement
131
+ # r_start = row_center - m // 2 # This is more for 'center' interpretation
132
+ # c_start = col_center - n // 2
133
+
134
+ # If peak is top-left:
135
+ r_start = row_center
136
+ c_start = col_center
137
+
138
+ # The original code's slice calculation:
139
+ # slice(row - m / 2, row + m / 2 + m_off), slice(col - n / 2, col + n / 2 + n_off)
140
+ # This implies the convolved peak (row, col) is treated as the center of the submatrix.
141
+ # Let's stick to that interpretation for now.
142
+
143
+ m_half1 = m // 2
144
+ m_half2 = m - m_half1
145
+ n_half1 = n // 2
146
+ n_half2 = n - n_half1
147
+
148
+ # Potential slice indices
149
+ r1 = row_center - m_half1
150
+ r2 = row_center + m_half2
151
+ c1 = col_center - n_half1
152
+ c2 = col_center + n_half2
153
+
154
+ # Ensure slices are within bounds of A
155
+ # This step is crucial if 'same' padding leads to centers near edges
156
+ r1 = max(0, r1)
157
+ c1 = max(0, c1)
158
+ r2 = min(M, r2) # slice goes up to M-1
159
+ c2 = min(N, c2) # slice goes up to N-1
160
+
161
+ if r1 >= r2 or c1 >= c2: # Invalid or empty slice
162
+ continue
163
+
164
+ this_location: SlicePair = (slice(r1, r2), slice(c1, c2))
165
+
166
+ # Sum of the submatrix at this_location
167
+ # This sum is the actual value, not convolved.argmax()
168
+ current_sum: Numeric = A[this_location].sum()
169
+
170
+ if current_sum >= max_value:
171
+ max_value = current_sum
172
+ location = this_location
173
+
174
+ # Final local search refinement if a valid location was found
175
+ if location != (slice(0,0), slice(0,0)) and max_value != -np.inf :
176
+ location, max_value = local_search(A, location)
177
+
178
+ t: float = time.time() - t0
179
  return location, max_value, t
run.py CHANGED
@@ -1,17 +1,25 @@
1
  from algorithms import brute_submatrix_max, fft_submatrix_max
2
  import numpy as np
 
 
 
 
 
3
 
4
  # Set matrix dimensions (rows, columns)
5
- M, N = 64, 64
 
6
 
7
  # Generate MxN matrix of random integers
8
- A = np.random.randint(-100, 100, size=(M, N))
9
 
10
  # Test each algorithm
11
  # output format: maximizing subarray slice specification, maximum sum
12
  # value, running time
13
- print
14
- print "Running FFT algorithm:"
15
- print fft_submatrix_max(A)
16
- print "Running brute force algorithm:"
17
- print brute_submatrix_max(A)
 
 
 
1
  from algorithms import brute_submatrix_max, fft_submatrix_max
2
  import numpy as np
3
+ from typing import Tuple, Any
4
+
5
+ # Define type aliases for clarity
6
+ SlicePair = Tuple[slice, slice]
7
+ Numeric = Any # Matches algorithms.py
8
 
9
  # Set matrix dimensions (rows, columns)
10
+ M: int = 64
11
+ N: int = 64
12
 
13
  # Generate MxN matrix of random integers
14
+ A: np.ndarray = np.random.randint(-100, 100, size=(M, N))
15
 
16
  # Test each algorithm
17
  # output format: maximizing subarray slice specification, maximum sum
18
  # value, running time
19
+ print()
20
+ print("Running FFT algorithm:")
21
+ result_fft: Tuple[SlicePair, Numeric, float] = fft_submatrix_max(A)
22
+ print(result_fft)
23
+ print("Running brute force algorithm:")
24
+ result_brute: Tuple[SlicePair, Numeric, float] = brute_submatrix_max(A)
25
+ print(result_brute)