Skip to content

Commit 99620ba

Browse files
using the chain name instead of index for clarity
1 parent 1fd20a8 commit 99620ba

1 file changed

Lines changed: 89 additions & 48 deletions

File tree

src/gwkokab_scripts/delete_chains.py

Lines changed: 89 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,94 +2,134 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44

5+
import re
6+
57
import h5py
68

79

8-
def _delete_chains(f: h5py.File, chains_idx: list[int], n_chains: int) -> None:
10+
def _parse_chain_names(chains: list[str]) -> list[str]:
11+
"""Parse chain identifiers supplied as names like chain_0."""
12+
chain_names: list[str] = []
13+
for chain in chains:
14+
value = str(chain).strip()
15+
if re.fullmatch(r"chain_\d+", value):
16+
chain_names.append(value)
17+
continue
18+
19+
raise ValueError(
20+
f"Invalid chain identifier '{chain}'. Use the full chain name, for example chain_0."
21+
)
22+
return chain_names
23+
24+
25+
def _delete_chains(
26+
f: h5py.File,
27+
chains_to_delete: list[str],
28+
chain_names: list[str],
29+
) -> None:
930
import numpy as np
1031

1132
from gwkokab.analysis.utils.literals import SAMPLES_GROUP_NAME
1233

1334
samples = f[SAMPLES_GROUP_NAME][()]
1435
del f[SAMPLES_GROUP_NAME]
1536

37+
n_chains = len(chain_names)
1638
cleaned_chains_flatten = np.vstack([
1739
chain
18-
for idx, chain in enumerate(np.array_split(samples, n_chains, axis=0))
19-
if idx not in chains_idx
40+
for chain_name, chain in zip(
41+
chain_names, np.array_split(samples, n_chains, axis=0), strict=True
42+
)
43+
if chain_name not in chains_to_delete
2044
])
2145

2246
f.create_dataset(SAMPLES_GROUP_NAME, data=cleaned_chains_flatten)
2347

2448

25-
def _dc_sampler_is_numpyro(f: h5py.File, chains_idx: list[int]) -> None:
26-
n_chains = f["/sampler_cfg/mcmc"].attrs["num_chains"]
27-
f["/sampler_cfg/mcmc"].attrs["num_chains"] = n_chains - len(chains_idx)
28-
chains = [
29-
f["/chains/chain_{}".format(idx)][()]
30-
for idx in range(n_chains)
31-
if idx not in chains_idx
49+
def _dc_sampler_is_numpyro(f: h5py.File, chains_to_delete: list[str]) -> list[str]:
50+
chain_names = sorted(f["/chains"].keys(), key=lambda name: int(name.split("_")[-1]))
51+
remaining_chain_names = [
52+
chain_name for chain_name in chain_names if chain_name not in chains_to_delete
3253
]
3354

34-
from gwkokab.analysis.core.utils import write_to_hdf5
55+
for chain_name in chains_to_delete:
56+
del f[f"/chains/{chain_name}"]
3557

36-
del f["/chains"]
58+
f["/sampler_cfg/mcmc"].attrs["num_chains"] = len(remaining_chain_names)
3759

38-
for idx, chain in enumerate(chains):
39-
write_to_hdf5(f, f"/chains/chain_{idx}", chain)
60+
return chain_names
4061

4162

42-
def _dc_sampler_is_flowMC(f: h5py.File, chains_idx: list[int]) -> None:
43-
n_chains = f["sampler_cfg"].attrs["n_chains"]
44-
f["sampler_cfg"].attrs["n_chains"] = n_chains - len(chains_idx)
63+
def _dc_sampler_is_flowMC(f: h5py.File, chains_to_delete: list[str]) -> list[str]:
64+
phase_chain_names: list[list[str]] = []
4565

46-
from gwkokab.analysis.core.utils import write_to_hdf5
66+
for phase in ("train", "prod"):
67+
phase_group = f[f"/chains/{phase}"]
68+
chain_names = sorted(
69+
phase_group.keys(), key=lambda name: int(name.split("_")[-1])
70+
)
71+
phase_chain_names.append(chain_names)
72+
73+
if phase_chain_names[0] != phase_chain_names[1]:
74+
raise ValueError(
75+
f"flowMC train/prod chain names do not match: {phase_chain_names}"
76+
)
77+
78+
chain_names = phase_chain_names[0]
79+
remaining_chain_names = [
80+
chain_name for chain_name in chain_names if chain_name not in chains_to_delete
81+
]
4782

4883
for phase in ("train", "prod"):
49-
chains = [
50-
f[f"/chains/{phase}/chain_{idx}"]
51-
for idx in range(n_chains)
52-
if idx not in chains_idx
53-
]
84+
for chain_name in chains_to_delete:
85+
del f[f"/chains/{phase}/{chain_name}"]
5486

55-
del f[f"/chains/{phase}"]
87+
f["sampler_cfg"].attrs["n_chains"] = len(remaining_chain_names)
5688

57-
for idx, chain in enumerate(chains):
58-
group_name = f"/chains/{phase}/chain_{idx}"
59-
f.create_group(group_name)
60-
for key in chain.keys():
61-
write_to_hdf5(f, f"{group_name}/{key}", chain[key][()])
89+
return chain_names
6290

6391

64-
def _delete_chains_factory(f: h5py.File, chains_idx: list[int]) -> None:
65-
n_chains = 0
92+
def _delete_chains_factory(f: h5py.File, chains_to_delete: list[str]) -> None:
6693
sampler_name = f["sampler_cfg"].attrs["sampler_name"]
6794

6895
if sampler_name == "numpyro":
69-
n_chains = len(f["chains"].keys())
96+
chain_names = sorted(
97+
f["/chains"].keys(), key=lambda name: int(name.split("_")[-1])
98+
)
7099
elif sampler_name == "flowMC":
71-
n_chains = int(f["sampler_cfg"].attrs["n_chains"])
100+
phase_chain_names = [
101+
sorted(
102+
f[f"/chains/{phase}"].keys(), key=lambda name: int(name.split("_")[-1])
103+
)
104+
for phase in ("train", "prod")
105+
]
106+
if phase_chain_names[0] != phase_chain_names[1]:
107+
raise ValueError(
108+
f"flowMC train/prod chain names do not match: {phase_chain_names}"
109+
)
110+
chain_names = phase_chain_names[0]
72111
else:
73112
raise ValueError(f"Unrecognized Sampler: {sampler_name}")
74113

75-
unique_chains_idx = set(chains_idx)
76-
if not unique_chains_idx:
77-
raise ValueError("No chain indices specified for deletion.")
78-
if any(idx < 0 or idx >= n_chains for idx in unique_chains_idx):
114+
unique_chains_to_delete = set(chains_to_delete)
115+
if not unique_chains_to_delete:
116+
raise ValueError("No chain names specified for deletion.")
117+
missing_chains = sorted(unique_chains_to_delete - set(chain_names))
118+
if missing_chains:
79119
raise ValueError(
80-
f"Chain indices must be between 0 and {n_chains - 1}. Got {chains_idx}"
120+
f"Requested chains do not exist: {missing_chains}. Available chains: {chain_names}"
81121
)
82-
if len(unique_chains_idx) >= n_chains:
122+
if len(unique_chains_to_delete) >= len(chain_names):
83123
raise ValueError(
84-
f"Cannot delete all chains. Total chains: {n_chains}, requested to delete: {len(unique_chains_idx)}"
124+
f"Cannot delete all chains. Total chains: {len(chain_names)}, requested to delete: {len(unique_chains_to_delete)}"
85125
)
86126

87127
if sampler_name == "numpyro":
88-
_dc_sampler_is_numpyro(f, chains_idx)
128+
original_chain_names = _dc_sampler_is_numpyro(f, chains_to_delete)
89129
elif sampler_name == "flowMC":
90-
_dc_sampler_is_flowMC(f, chains_idx)
130+
original_chain_names = _dc_sampler_is_flowMC(f, chains_to_delete)
91131

92-
_delete_chains(f, chains_idx, n_chains)
132+
_delete_chains(f, chains_to_delete, original_chain_names)
93133

94134

95135
def main():
@@ -102,10 +142,11 @@ def main():
102142

103143
parser.add_argument(
104144
"-n",
145+
"--chains",
105146
nargs="+",
106-
type=int,
147+
type=str,
107148
required=True,
108-
help="List of chain numbers to delete. Chain numbering starts from 0.",
149+
help="List of chains to delete. Use full names like chain_0 chain_2.",
109150
)
110151
parser.add_argument(
111152
"-i",
@@ -116,7 +157,7 @@ def main():
116157
parser.add_argument(
117158
"-o",
118159
type=str,
119-
required=True,
160+
default="clean_data.hdf5",
120161
help="Name of the output HDF5 file where the modified chains will be saved.",
121162
)
122163

@@ -137,9 +178,9 @@ def main():
137178

138179
input_filename = args.i
139180
output_filename = args.o
140-
numbers = args.n
181+
chains_to_delete = _parse_chain_names(args.chains)
141182

142183
shutil.copy(input_filename, output_filename)
143184

144185
with h5py.File(output_filename, "a") as f:
145-
_delete_chains_factory(f, numbers)
186+
_delete_chains_factory(f, chains_to_delete)

0 commit comments

Comments
 (0)