import time
import numpy as np
import scipy
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import paxplot
import pickle
from functools import partial
In [1]:
In [2]:
with open("data/sim_long_preprocessed_slice.pkl", "rb") as file:
= pickle.load(file)
import_dict import_dict.keys()
dict_keys(['parameters', 'patterns'])
In [3]:
= import_dict["patterns"][0]#[::2, ::2]
test_pattern
plt.imshow(test_pattern) test_pattern.shape
In [4]:
def get_adj_matrix_slow(pattern, eps=0.003):
assert pattern.shape[0] == pattern.shape[1]
= pattern.shape[0]
graph_res = graph_res**2
m
= np.zeros((m, m), dtype=np.float64)
adj_matrix = np.mean(pattern)
u_mean
for i in range(m):
= np.array([i - 1, i + 1, i - graph_res, i + graph_res])
neighbor_js = neighbor_js % m
neighbor_js
for j in neighbor_js:
= pattern[i % graph_res, i // graph_res]
u_vi = pattern[j % graph_res, j // graph_res]
u_vj = u_vi > u_mean
vi_high = u_vj > u_mean
vj_high if not vi_high ^ vj_high: # both on same side of u_mean
= 1
adj_matrix[i, j] else:
= eps
adj_matrix[i, j]
return adj_matrix
def compute_resistance_slow(pattern, eps):
assert pattern.shape[0] == pattern.shape[1]
= pattern.shape[0]
graph_res = graph_res**2
m
= get_adj_matrix_slow(pattern, eps)
adj_matrix = np.diag(adj_matrix @ np.ones((m,))) - adj_matrix
graph_laplacian = scipy.linalg.inv(np.ones((m, m)) + graph_laplacian)
K = np.ones((m, m)) * np.diagonal(K)[:, None]
Kvivi = Kvivi.T
Kvjvj = Kvivi + Kvjvj - 2 * K
resistance
assert resistance.shape == (m, m)
return resistance
= 0.003 eps
t1 = time.time() res_dist_reference = compute_resistance_slow(test_pattern, eps); time_reference = time.time() - t1 f”took {time_reference:.4f}s | mean={np.mean(res_dist_reference):.4f}”
In [5]:
= plt.subplots(figsize=(8,8), dpi=100)
fig, ax
= get_adj_matrix_slow(test_pattern, 0.5)
adj_matrix = adj_matrix.shape[0]
m = graph_laplacian = np.diag(adj_matrix @ np.ones((m,))) - adj_matrix
Z
= (0,m,0,m)
extent
=mpl.colors.TwoSlopeNorm(vcenter=0., vmin=Z.min(), vmax=Z.max())
divnorm= ax.imshow(Z, origin="upper", extent=extent, cmap="RdBu", norm=divnorm)
img = [0, 1*m//4, m//2, 3*m//4, m]
ticks
ax.set_xticks(ticks)
ax.set_xticklabels(ticks)
ax.set_yticks(ticks)-1])
ax.set_yticklabels(ticks[::
= 200
crop_size
# inset Axes....
= 0,crop_size,m-crop_size,m # subregion of the original image
x1, x2, y1, y2 = ax.inset_axes(
axins 0.3, 0.2, 0.9, 0.9],
[=(x1, x2), ylim=(y1, y2), xticklabels=[], yticklabels=[])
xlim="upper", extent=extent, cmap="RdBu", norm=divnorm)
axins.imshow(Z, origin
="black", lw=2)
ax.indicate_inset_zoom(axins, edgecolor
plt.show()
In [6]:
= import_dict["patterns"][0][:30:2, :30:2] test_pattern
In [7]:
= test_pattern.shape[0]
graph_res = graph_res**2
m = get_adj_matrix_slow(test_pattern, eps)
adj_matrix = np.diag(adj_matrix @ np.ones((m,))) - adj_matrix
graph_laplacian = scipy.linalg.inv(np.ones((m, m)) + graph_laplacian)
K = np.ones((m, m)) * np.diagonal(K)[:, None]
Kvivi = Kvivi.T
Kvjvj
plt.imshow(Kvjvj) plt.colorbar()
In [8]:
= time.time()
t1 = compute_resistance_slow(test_pattern, eps);
res_dist = time.time() - t1
time_reference f"took {time_reference:.4f}s" # | mean={np.mean(res_dist_reference):.4f}"
'took 0.1246s'
In [9]:
plt.imshow(res_dist); plt.colorbar()
In [10]:
def prepare_graph_laplacian_matrix(pattern, eps):
= pattern.shape[0]
graph_res = graph_res**2
m
= get_adj_matrix_slow(pattern, eps)
adj_matrix = np.diag(adj_matrix @ np.ones((m,))) - adj_matrix
graph_laplacian return graph_laplacian
= prepare_graph_laplacian_matrix(test_pattern, eps)
laplacian
# K = scipy.linalg.inv(np.ones((m, m)) + graph_laplacian)
In [11]:
%%timeit
scipy.linalg.pinv(laplacian)
142 ms ± 6.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [12]:
= scipy.linalg.pinv(laplacian) generalized_inverse
In [13]:
def check_g_inverse(A, G):
return np.isclose(A @ G @ A, A).mean() == 1.0
check_g_inverse(laplacian, generalized_inverse)
True
In [14]:
def benchmark_g_inv_fun(A, fun, num_loops=3):
= time.time()
t1 for i in range(num_loops):
= fun(A)
G = time.time() - t1
t_total assert check_g_inverse(A, G), "The provided function did not compute a correct g-inverse."
return t_total / num_loops
In [15]:
laplacian.shape
(225, 225)
In [16]:
benchmark_g_inv_fun(laplacian, np.linalg.pinv)
0.025146484375
In [17]:
benchmark_g_inv_fun(laplacian, scipy.linalg.pinv)
0.13741509119669595
In [18]:
def imqrginv(a: np.ndarray) -> np.ndarray:
= np.linalg.qr(a, mode="reduced")
q, r return r.transpose(1, 0).conj() @ np.linalg.inv(r @ r.transpose(1, 0).conj()) @ q.transpose(1, 0).conj()
benchmark_g_inv_fun(laplacian, np.linalg.pinv)
0.038936456044514976
In [19]:
def add_ones_before_fun(A, fun):
return fun(np.ones_like(A) + A)
= add_ones_before_fun(laplacian, np.linalg.inv)
maybe_g_inv assert check_g_inverse(laplacian, maybe_g_inv)
In [20]:
=np.linalg.inv)) benchmark_g_inv_fun(laplacian, partial(add_ones_before_fun, fun
0.0011440118153889973
In [21]:
=np.linalg.pinv)) benchmark_g_inv_fun(laplacian, partial(add_ones_before_fun, fun
0.009821097056070963
In [22]:
= {
g_inv_funs "+1-np-inv": partial(add_ones_before_fun, fun=np.linalg.inv),
"+1-scipy-inv": partial(add_ones_before_fun, fun=scipy.linalg.inv),
"np-pinv": np.linalg.pinv,
"scipy-pinv": scipy.linalg.pinv,
"np-pinvh": partial(np.linalg.pinv, hermitian=True),
"scipy-pinvh": scipy.linalg.pinvh,
}= {}
results_small_m for pattern_size in [8, 16, 32, 64]:
= import_dict["patterns"][0][:pattern_size, :pattern_size]
test_pattern = prepare_graph_laplacian_matrix(test_pattern, eps)
laplacian = laplacian.shape[0]
m print(f"graph has {m} vertices.")
= {}
results_small_m[m] for key, fun in g_inv_funs.items():
if "+1" in key: # repeat the faster algorithms a bit more often to reduce sampling error
if pattern_size < 30:
= 50
num_loops else:
= 20
num_loops else:
if pattern_size < 30:
= 20
num_loops else:
= 8
num_loops = benchmark_g_inv_fun(laplacian, fun, num_loops=num_loops)
elapsed print(elapsed, key)
= elapsed results_small_m[m][key]
graph has 64 vertices.
0.00011413097381591797 +1-np-inv
0.00014008522033691405 +1-scipy-inv
0.0012161016464233398 np-pinv
0.008746886253356933 scipy-pinv
0.002484130859375 np-pinvh
0.0036941170692443848 scipy-pinvh
graph has 256 vertices.
0.00465179443359375 +1-np-inv
0.00352142333984375 +1-scipy-inv
0.025093352794647215 np-pinv
0.16102132797241211 scipy-pinv
0.019651973247528078 np-pinvh
0.18433376550674438 scipy-pinvh
graph has 1024 vertices.
0.05806041955947876 +1-np-inv
0.06515189409255981 +1-scipy-inv
0.575680673122406 np-pinv
0.7435911595821381 scipy-pinv
0.20940321683883667 np-pinvh
0.9509794414043427 scipy-pinvh
graph has 4096 vertices.
2.1050000548362733 +1-np-inv
3.566044104099274 +1-scipy-inv
31.111353754997253 np-pinv
34.37004932761192 scipy-pinv
13.204486846923828 np-pinvh
69.60303673148155 scipy-pinvh
In [23]:
import warnings
"ignore", message="^.*is not officially supported by Paxplot, but it may still work.*$") warnings.filterwarnings(
In [24]:
= pd.DataFrame(results_small_m)
res_df = res_df.columns
cols = ["m="+str(col) for col in cols]
col_names
# Create figure
= paxplot.pax_parallel(n_axes=len(cols))
paxfig 2,], line_kwargs={"linestyle": "-"})
paxfig.plot(res_df.to_numpy()[:2:4], line_kwargs={"linestyle": "--"})
paxfig.plot(res_df.to_numpy()[-2:], line_kwargs={"linestyle": ":"})
paxfig.plot(res_df.to_numpy()[
paxfig.set_labels(col_names)"Elapsed time [s]", y=1.01)
plt.suptitle(
# Changing figure size
5, 3)
paxfig.set_size_inches(200)
paxfig.set_dpi(=0.1, bottom=0.2, right=0.9, top=0.9) # Padding
paxfig.subplots_adjust(left
# Change tick size
= 10
tick_size for ax in paxfig.axes:
="y", labelsize=tick_size)
ax.tick_params(axis
# Change label size
= 15
label_size for ax in paxfig.axes:
="x", labelsize=label_size)
ax.tick_params(axis
for i, m in enumerate(cols):
= res_df[m].max()
max_tick 0, max_tick * 1.01)
paxfig.set_lim(i,
paxfig.set_even_ticks(=i,
ax_idx=4,
n_ticks=0,
minimum=max_tick,
maximum
)
= paxfig.axes[0].lines
lines = [i for i in res_df.index]
labels =(1.5, 1),
plt.legend(lines, labels, bbox_to_anchor='upper left', borderaxespad=3.5); loc
In [25]:
= {
g_inv_funs "+1-np-inv": partial(add_ones_before_fun, fun=np.linalg.inv),
"+1-scipy-inv": partial(add_ones_before_fun, fun=scipy.linalg.inv),
}= {}
results_large_m for pattern_size in [16, 32, 64, 128]:
= import_dict["patterns"][0][:pattern_size, :pattern_size]
test_pattern = prepare_graph_laplacian_matrix(test_pattern, eps)
laplacian = laplacian.shape[0]
m print(f"graph has {m} vertices.")
= {}
results_large_m[m] for key, fun in g_inv_funs.items():
= benchmark_g_inv_fun(laplacian, fun, num_loops=3)
elapsed print(elapsed, key)
= elapsed results_large_m[m][key]
graph has 256 vertices.
0.0024029413859049478 +1-np-inv
0.041563828786214195 +1-scipy-inv
graph has 1024 vertices.
0.546661376953125 +1-np-inv
0.06827251116434734 +1-scipy-inv
graph has 4096 vertices.
2.0935885111490884 +1-np-inv
1.9664779504140217 +1-scipy-inv
graph has 16384 vertices.
82.14190308252971 +1-np-inv
99.59056385358174 +1-scipy-inv
In [26]:
= pd.DataFrame(results_large_m)
res_df = res_df.columns
cols = ["m="+str(col) for col in cols]
col_names
# Create figure
= paxplot.pax_parallel(n_axes=len(cols))
paxfig ={"linestyle": "-"})
paxfig.plot(res_df.to_numpy(), line_kwargs
paxfig.set_labels(col_names)"Elapsed time [s]", y=1.01)
plt.suptitle(
# Changing figure size
5, 3)
paxfig.set_size_inches(200)
paxfig.set_dpi(=0.1, bottom=0.2, right=0.9, top=0.9) # Padding
paxfig.subplots_adjust(left
# Change tick size
= 10
tick_size for ax in paxfig.axes:
="y", labelsize=tick_size)
ax.tick_params(axis
# Change label size
= 12
label_size for ax in paxfig.axes:
="x", labelsize=label_size)
ax.tick_params(axis
for i, m in enumerate(cols):
= res_df[m].max()
max_tick 0, max_tick * 1.01)
paxfig.set_lim(i,
paxfig.set_even_ticks(=i,
ax_idx=4,
n_ticks=0,
minimum=max_tick,
maximum
)
= paxfig.axes[0].lines
lines = [i for i in res_df.index]
labels =(1.5, 1),
plt.legend(lines, labels, bbox_to_anchor='upper left', borderaxespad=3.5); loc