Skip to content

Commit 7dcc344

Browse files
committed
Separate Lanczos_PRO, add sketch of memory profiling
1 parent 588df88 commit 7dcc344

File tree

13 files changed

+369
-347
lines changed

13 files changed

+369
-347
lines changed

.gitignore

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,22 @@ cython_debug/
174174
*.dat
175175
*.data
176176

177+
# other data files and logs
178+
data/*
179+
logs/*
180+
177181
# temporary folders used for profiling
178182
tmp_data/*
183+
*tmp*
179184

180185
# for documentation
181186
docs/_*
182187

183-
#vscode files
188+
# vscode files
184189
.vscode/*
185190

186-
#skbuild folder
191+
# skbuild folder
187192
skbuild/*
188-
data/*
193+
194+
# temporary files for vim users
189195
*.swp

experiments/config.yaml

Lines changed: 0 additions & 4 deletions
This file was deleted.

experiments/config_accuracy.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
max_iter: 1000
2+
tol: 0.0001

experiments/config_profiling.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dim: 20000
2+
density: 0.2

scripts/profiling.py

Lines changed: 93 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,117 @@
1-
import cupyx.scipy.sparse as cpsp
2-
import cupy as cp
3-
from pyclassify import (
4-
eigenvalues_np,
5-
eigenvalues_sp,
6-
# eigenvalues_cp,
7-
power_method,
8-
power_method_numba,
9-
# power_method_cp,
10-
QR,
11-
# QR_cp,
12-
)
13-
from pyclassify.utils import (
14-
make_symmetric,
15-
read_config,
16-
profile_with_cprofile,
17-
# profile_with_cupy_profiler,
18-
)
1+
from pyclassify import power_method, power_method_numba
2+
from pyclassify.utils import make_symmetric, read_config
3+
from pyclassify.profiling_MPI import mpi_profiled, get_memory_usage_mb, profile_serial
194
import numpy as np
205
import scipy.sparse as sp
21-
import scipy
226
import random
237
import argparse
8+
import os
9+
import time
10+
import pandas as pd
11+
from mpi4py import MPI
2412

2513

14+
# Seed for reproducibility
2615
seed = 8422
2716
random.seed(seed)
28-
# cp.random.seed(seed)
2917
np.random.seed(seed)
3018

19+
# Some MPI info
20+
comm = MPI.COMM_WORLD
21+
rank = comm.Get_rank()
22+
n_procs = comm.Get_size()
3123

24+
25+
# Here we parse the arguments. We provide a default value, but the user is free to chose another config file.
3226
parser = argparse.ArgumentParser()
3327
parser.add_argument("--config", type=str, required=False, help="config file:")
34-
3528
args = parser.parse_args()
36-
filename = (
37-
args.config if args.config else "./experiments/config"
38-
) # automatic choice if no argument is passed
29+
config_file = args.config if args.config else "./experiments/config_profiling"
30+
31+
32+
# Now read the config only if the rank is 0. We will broadcast the info to all other ranks.
33+
if rank == 0:
34+
kwargs = read_config(config_file)
35+
dim = kwargs["dim"]
36+
density = kwargs["density"]
37+
else:
38+
dim = None
39+
density = None
40+
41+
dim = comm.bcast(dim, root=0)
42+
density = comm.bcast(density, root=0)
43+
44+
45+
# Generate the data
46+
if rank == 0:
47+
A = sp.random(dim, dim, density=density, format="csr")
48+
A = make_symmetric(A)
49+
else:
50+
A = None
51+
52+
A = comm.bcast(A, root=0)
53+
54+
55+
# Now we start profiling. Notice that the only function that requires MPI is the one that is not profiled within a 'if rank==0' statement.
56+
57+
# @mpi_profiled
58+
# def profiled_divide_et_impera(A):
59+
# from pyclassify import divide_et_impera # avoid circular import
60+
# return divide_et_impera(A)
61+
62+
results = {}
63+
64+
65+
if rank == 0:
66+
_ = profile_serial(power_method_numba, A.toarray())
67+
results["power_method"] = profile_serial(power_method, A)
68+
results["power_method_numba"] = profile_serial(power_method_numba, A.toarray())
69+
# results["QR"] = profile_serial(QR, A)
3970

71+
# mpi_result_QR = profiled_QR(A)
72+
# mpi_result_divide = profiled_divide_et_impera(A)
4073

41-
kwargs = read_config(filename)
42-
dim = kwargs["dim"]
43-
density = kwargs["density"]
44-
tol = kwargs["tol"]
45-
max_iter = kwargs["max_iter"]
74+
# if rank == 0:
75+
# results["QR"] = mpi_result_QR
76+
# results["divide_et_impera"] = mpi_result_divide
4677

4778

48-
eigenvals = np.arange(1, dim + 1)
49-
A = np.diag(eigenvals)
50-
U = scipy.stats.ortho_group.rvs(dim)
51-
A = U @ A @ U.T
52-
A = make_symmetric(A)
53-
A = sp.csr_matrix(A)
54-
# A_cp = cpsp.csr_matrix(A)
79+
# Now we just save to CSV.
80+
if rank == 0:
81+
os.makedirs("logs", exist_ok=True)
82+
mem_csv = "logs/memory.csv"
83+
time_csv = "logs/time.csv"
5584

85+
mem_row = {
86+
"matrix_size": dim,
87+
"density": density,
88+
"num_procs": n_procs,
89+
**{key: results[key]["memory_total"] for key in results},
90+
}
5691

57-
log_file = "./logs/timings.csv"
58-
iteration_factor = 300
92+
time_row = {
93+
"matrix_size": dim,
94+
"density": density,
95+
"num_procs": n_procs,
96+
**{key: results[key]["time"] for key in results},
97+
}
5998

99+
def append_or_create_csv(path, row, columns):
100+
"""
101+
This helper function just decides whether to append to an existing CSV or to create a new one.
102+
"""
103+
if not os.path.exists(path):
104+
pd.DataFrame([row]).to_csv(path, index=False, columns=columns)
105+
else:
106+
df = pd.read_csv(path)
107+
df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
108+
df.to_csv(path, index=False, columns=columns)
60109

61-
profile_with_cprofile(
62-
log_file, dim, "eigenvalues_np", eigenvalues_np, A.toarray(), symmetric=True
63-
)
64-
profile_with_cprofile(
65-
log_file, dim, "eigenvalues_sp", eigenvalues_sp, A, symmetric=True
66-
)
67-
profile_with_cprofile(log_file, dim, "power_method", power_method, A)
68-
profile_with_cprofile(
69-
log_file, dim, "power_method_numba", power_method_numba, A.toarray()
70-
)
71-
profile_with_cprofile(
72-
log_file, dim, "QR", QR, A.toarray(), max_iter=iteration_factor * dim
73-
)
110+
method_names = list(results.keys())
111+
base_cols = ["matrix_size", "density", "num_procs"]
112+
all_columns = base_cols + method_names
74113

114+
append_or_create_csv(mem_csv, mem_row, all_columns)
115+
append_or_create_csv(time_csv, time_row, all_columns)
75116

76-
# profile_with_cupy_profiler(log_file, dim, "eigenvalues_cp", eigenvalues_cp, A_cp)
77-
# profile_with_cupy_profiler(log_file, dim, "power_method_cp", power_method_cp, A_cp)
78-
# profile_with_cupy_profiler(log_file, dim, "QR_cp", QR_cp, A_cp, q0=cp.random.rand(dim), tol=1e-3, max_iter=iteration_factor * dim)
117+
print("Done! The results have been saved to logs/memory.csv and logs/time.csv")

scripts/run.py

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +0,0 @@
1-
# import cupyx.scipy.sparse as cpsp
2-
# import cupy as cp
3-
from pyclassify import (
4-
eigenvalues_np,
5-
eigenvalues_sp,
6-
# eigenvalues_cp,
7-
power_method,
8-
power_method_numba,
9-
# power_method_cp,
10-
QR,
11-
# QR_cp,
12-
)
13-
from pyclassify.utils import (
14-
make_symmetric,
15-
read_config,
16-
profile_with_cprofile,
17-
# profile_with_cupy_profiler,
18-
)
19-
import numpy as np
20-
import scipy.sparse as sp
21-
import scipy
22-
import random
23-
import argparse
24-
25-
26-
# cp.cuda.Device(0).use()
27-
# cp.get_default_memory_pool().free_all_blocks()
28-
29-
30-
seed = 8422
31-
random.seed(seed)
32-
# cp.random.seed(seed)
33-
np.random.seed(seed)
34-
35-
36-
parser = argparse.ArgumentParser()
37-
parser.add_argument("--config", type=str, required=False, help="config file:")
38-
39-
args = parser.parse_args()
40-
filename = (
41-
args.config if args.config else "./experiments/config"
42-
) # automatic choice if no argument is passed
43-
44-
45-
kwargs = read_config(filename)
46-
dim = kwargs["dim"]
47-
density = kwargs["density"]
48-
tol = kwargs["tol"]
49-
max_iter = kwargs["max_iter"]
50-
51-
52-
eigenvals = np.arange(1, dim + 1)
53-
A = np.diag(eigenvals)
54-
U = scipy.stats.ortho_group.rvs(dim)
55-
A = U @ A @ U.T
56-
A = make_symmetric(A)
57-
A = sp.csr_matrix(A)
58-
# A_cp = cpsp.csr_matrix(A)
59-
60-
61-
log_file = "./logs/timings.csv"
62-
iteration_factor = 300
63-
64-
65-
profile_with_cprofile(
66-
log_file, dim, "eigenvalues_np", eigenvalues_np, A.toarray(), symmetric=True
67-
)
68-
profile_with_cprofile(
69-
log_file, dim, "eigenvalues_sp", eigenvalues_sp, A, symmetric=True
70-
)
71-
profile_with_cprofile(log_file, dim, "power_method", power_method, A)
72-
profile_with_cprofile(
73-
log_file, dim, "power_method_numba", power_method_numba, A.toarray()
74-
)
75-
profile_with_cprofile(
76-
log_file, dim, "QR", QR, A.toarray(), max_iter=iteration_factor * dim
77-
)
78-
79-
# profile_with_cupy_profiler(log_file, dim, "eigenvalues_cp", eigenvalues_cp, A_cp)
80-
# profile_with_cupy_profiler(log_file, dim, "power_method_cp", power_method_cp, A_cp)
81-
# profile_with_cupy_profiler(log_file, dim, "QR_cp", QR_cp, A_cp, q0=cp.random.rand(dim), tol=1e-3, max_iter=iteration_factor * dim)

shell/profile.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
#!/bin/bash
2-
python -m kernprof -l -o logs/profile_eigenvalues.dat scripts/run.py --config=experiments/config
3-
python -m line_profiler -rmt "logs/profile_eigenvalues.dat" > logs/eigenvalues.txt

shell/submit.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1+
#!/bin/bash
12
module load cmake/3.29.1
23
module load intel/2021.2
3-
# Comment the following line if you are not interested in running using MPI
44
module load openmpi3/3.1.4

src/pyclassify/__init__.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
__all__ = [
22
"eigenvalues_np",
33
"eigenvalues_sp",
4-
# "eigenvalues_cp",
54
"power_method",
65
"power_method_numba",
7-
# "power_method_cp",
6+
"Lanczos_PRO",
87
"EigenSolver",
9-
# "Lanczos_PRO_cp",
10-
# "QR_method_cp",
11-
# "QR_cp",
128
]
139

1410
from .QR_cpp import QR_algorithm, Eigen_value_calculator
1511

1612
from .eigenvalues import (
1713
eigenvalues_np,
1814
eigenvalues_sp,
19-
# eigenvalues_cp,
2015
power_method,
2116
power_method_numba,
22-
# power_method_cp,
17+
Lanczos_PRO,
2318
EigenSolver,
24-
# Lanczos_PRO_cp,
25-
# QR_method_cp,
26-
# QR_cp,
2719
)
2820

29-
from .zero_finder import compute_Psi, secular_solver
21+
from .zero_finder import compute_Psi, secular_solver

0 commit comments

Comments
 (0)