diff --git a/examples/crystals/README.md b/examples/crystals/README.md new file mode 100644 index 000000000..91a97f647 --- /dev/null +++ b/examples/crystals/README.md @@ -0,0 +1,43 @@ +## Crystals + +A simple example to train CGCNN based models to predict material properties. + +Make sure to activate the conda environment: + +```console +conda activate gt4sd +``` + +To launch a training execute the following command from the GT4SD root. Examples of a dataset, including he sample-classification that we use in the given example, can be found in the CGCNN's official implementation repo in the following [link](https://github.com/txie-93/cgcnn/tree/master/data). + +```console + +gt4sd-trainer --training_pipeline_name cgcnn \ + --task classification \ + --datapath sample-classification \ + --atom_fea_len 64 \ + --h_fea_len 128 \ + --n_conv 3 \ + --n_h 1 \ + --epochs 30 \ + --batch_size 256 \ + --lr 0.01 \ + --momentum 0.9 \ + --weight_decay 0.0 \ + --optim SGD +``` + +The code is adapted from: . + +```bibtex +@article{xie2018crystal, + title={Crystal graph convolutional neural networks for an accurate and interpretable prediction of material properties}, + author={Xie, Tian and Grossman, Jeffrey C}, + journal={Physical review letters}, + volume={120}, + number={14}, + pages={145301}, + year={2018}, + publisher={APS} +} +``` diff --git a/notebooks/cgcnn-demo.ipynb b/notebooks/cgcnn-demo.ipynb new file mode 100644 index 000000000..8d868a352 --- /dev/null +++ b/notebooks/cgcnn-demo.ipynb @@ -0,0 +1,228 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Inference using CGCNN Model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this notebook we show how to perform inference using GT4SD and CGCNN-based models. The current existing models (algorithm_version=v0) and the sample dataset have been obtained from the [official CGCNN repository](https://github.com/txie-93/cgcnn)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Formation Energy\n", + "\n", + "This method predicts the formation energy per atom using the CGCNN framework (unit eV/atom)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import FormationEnergyParameters, FormationEnergy\n", + "\n", + "model_parameters = FormationEnergyParameters(algorithm_version=\"v0\")\n", + "model = FormationEnergy(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Absolute Energy\n", + "\n", + "This method predicts the absolute energy of crystals using the CGCNN framework (unit eV/atom)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import AbsoluteEnergy, AbsoluteEnergyParameters\n", + "\n", + "model_parameters = AbsoluteEnergyParameters(algorithm_version=\"v0\")\n", + "model = AbsoluteEnergy(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Band Gap\n", + "\n", + "This method predicts the band gap of crystals using the CGCNN framework (unit eV)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import BandGapParameters, BandGap\n", + "\n", + "model_parameters = BandGapParameters(algorithm_version=\"v0\")\n", + "model = BandGap(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fermi Energy\n", + "\n", + "This method predicts the Fermi energy of crystals using the CGCNN framework (unit eV/atom)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import FermiEnergyParameters, FermiEnergy\n", + "\n", + "model_parameters = FermiEnergyParameters(algorithm_version=\"v0\")\n", + "model = FermiEnergy(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bulk Moduli\n", + "\n", + "This method predicts the bulk moduli of crystals using the CGCNN framework (unit log(GPa))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import BulkModuliParameters, BulkModuli\n", + "\n", + "model_parameters = BulkModuliParameters(algorithm_version=\"v0\")\n", + "model = BulkModuli(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Shear Moduli\n", + "\n", + "This method predicts the shear moduli of crystals using the CGCNN framework (unit log(GPa))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import ShearModuliParameters, ShearModuli\n", + "\n", + "model_parameters = ShearModuliParameters(algorithm_version=\"v0\")\n", + "model = ShearModuli(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Poisson Ratio\n", + "\n", + "This method predicts the poisson ratio of crystals using the CGCNN framework." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import PoissonRatioParameters, PoissonRatio\n", + "\n", + "model_parameters = PoissonRatioParameters(algorithm_version=\"v0\")\n", + "model = PoissonRatio(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Metal-Semiconductor classifier\n", + "\n", + "This method predicts if the provided crystals are metal (1) or semiconductors (0) using the CGCNN framework." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gt4sd.properties.crystals.core import MetalSemiconductorClassifierParameters, MetalSemiconductorClassifier\n", + "\n", + "model_parameters = MetalSemiconductorClassifierParameters(algorithm_version=\"v0\")\n", + "model = MetalSemiconductorClassifier(model_parameters)\n", + "\n", + "model(input=\"cgcnn-sample\")" + ] + } + ], + "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.8.15" + }, + "vscode": { + "interpreter": { + "hash": "7f6df040e92be56c42e1ba5ffdd58832f97e24eff2af0498cbc88845f2e95a48" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/cgcnn-sample/1000041.cif b/notebooks/cgcnn-sample/1000041.cif new file mode 100644 index 000000000..d2aac1f0a --- /dev/null +++ b/notebooks/cgcnn-sample/1000041.cif @@ -0,0 +1,259 @@ +#------------------------------------------------------------------------------ +#$Date: 2015-01-27 21:58:39 +0200 (Tue, 27 Jan 2015) $ +#$Revision: 130149 $ +#$URL: svn://www.crystallography.net/cod/cif/1/00/00/1000041.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/ +# +# All data on this site have been placed in the public domain by the +# contributors. +# +data_1000041 +loop_ +_publ_author_name +'Abrahams, S C' +'Bernstein, J L' +_publ_section_title +; +Accuracy of an automatic diffractometer. measurement of the sodium +chloride structure factors +; +_journal_coden_ASTM ACCRA9 +_journal_name_full 'Acta Crystallographica (1,1948-23,1967)' +_journal_page_first 926 +_journal_page_last 932 +_journal_paper_doi 10.1107/S0365110X65002244 +_journal_volume 18 +_journal_year 1965 +_chemical_formula_structural 'Na Cl' +_chemical_formula_sum 'Cl Na' +_chemical_name_systematic 'Sodium chloride' +_space_group_IT_number 225 +_symmetry_cell_setting cubic +_symmetry_Int_Tables_number 225 +_symmetry_space_group_name_Hall '-F 4 2 3' +_symmetry_space_group_name_H-M 'F m -3 m' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_formula_units_Z 4 +_cell_length_a 5.62 +_cell_length_b 5.62 +_cell_length_c 5.62 +_cell_volume 177.5 +_refine_ls_R_factor_all 0.022 +_cod_database_code 1000041 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +y,z,x +z,x,y +x,z,y +y,x,z +z,y,x +x,-y,-z +y,-z,-x +z,-x,-y +x,-z,-y +y,-x,-z +z,-y,-x +-x,y,-z +-y,z,-x +-z,x,-y +-x,z,-y +-y,x,-z +-z,y,-x +-x,-y,z +-y,-z,x +-z,-x,y +-x,-z,y +-y,-x,z +-z,-y,x +-x,-y,-z +-y,-z,-x +-z,-x,-y +-x,-z,-y +-y,-x,-z +-z,-y,-x +-x,y,z +-y,z,x +-z,x,y +-x,z,y +-y,x,z +-z,y,x +x,-y,z +y,-z,x +z,-x,y +x,-z,y +y,-x,z +z,-y,x +x,y,-z +y,z,-x +z,x,-y +x,z,-y +y,x,-z +z,y,-x +x,1/2+y,1/2+z +1/2+x,y,1/2+z +1/2+x,1/2+y,z +y,1/2+z,1/2+x +1/2+y,z,1/2+x +1/2+y,1/2+z,x +z,1/2+x,1/2+y +1/2+z,x,1/2+y +1/2+z,1/2+x,y +x,1/2+z,1/2+y +1/2+x,z,1/2+y +1/2+x,1/2+z,y +y,1/2+x,1/2+z +1/2+y,x,1/2+z +1/2+y,1/2+x,z +z,1/2+y,1/2+x +1/2+z,y,1/2+x +1/2+z,1/2+y,x +x,1/2-y,1/2-z +1/2+x,-y,1/2-z +1/2+x,1/2-y,-z +y,1/2-z,1/2-x +1/2+y,-z,1/2-x +1/2+y,1/2-z,-x +z,1/2-x,1/2-y +1/2+z,-x,1/2-y +1/2+z,1/2-x,-y +x,1/2-z,1/2-y +1/2+x,-z,1/2-y +1/2+x,1/2-z,-y +y,1/2-x,1/2-z +1/2+y,-x,1/2-z +1/2+y,1/2-x,-z +z,1/2-y,1/2-x +1/2+z,-y,1/2-x +1/2+z,1/2-y,-x +-x,1/2+y,1/2-z +1/2-x,y,1/2-z +1/2-x,1/2+y,-z +-y,1/2+z,1/2-x +1/2-y,z,1/2-x +1/2-y,1/2+z,-x +-z,1/2+x,1/2-y +1/2-z,x,1/2-y +1/2-z,1/2+x,-y +-x,1/2+z,1/2-y +1/2-x,z,1/2-y +1/2-x,1/2+z,-y +-y,1/2+x,1/2-z +1/2-y,x,1/2-z +1/2-y,1/2+x,-z +-z,1/2+y,1/2-x +1/2-z,y,1/2-x +1/2-z,1/2+y,-x +-x,1/2-y,1/2+z +1/2-x,-y,1/2+z +1/2-x,1/2-y,z +-y,1/2-z,1/2+x +1/2-y,-z,1/2+x +1/2-y,1/2-z,x +-z,1/2-x,1/2+y +1/2-z,-x,1/2+y +1/2-z,1/2-x,y +-x,1/2-z,1/2+y +1/2-x,-z,1/2+y +1/2-x,1/2-z,y +-y,1/2-x,1/2+z +1/2-y,-x,1/2+z +1/2-y,1/2-x,z +-z,1/2-y,1/2+x +1/2-z,-y,1/2+x +1/2-z,1/2-y,x +-x,1/2-y,1/2-z +1/2-x,-y,1/2-z +1/2-x,1/2-y,-z +-y,1/2-z,1/2-x +1/2-y,-z,1/2-x +1/2-y,1/2-z,-x +-z,1/2-x,1/2-y +1/2-z,-x,1/2-y +1/2-z,1/2-x,-y +-x,1/2-z,1/2-y +1/2-x,-z,1/2-y +1/2-x,1/2-z,-y +-y,1/2-x,1/2-z +1/2-y,-x,1/2-z +1/2-y,1/2-x,-z +-z,1/2-y,1/2-x +1/2-z,-y,1/2-x +1/2-z,1/2-y,-x +-x,1/2+y,1/2+z +1/2-x,y,1/2+z +1/2-x,1/2+y,z +-y,1/2+z,1/2+x +1/2-y,z,1/2+x +1/2-y,1/2+z,x +-z,1/2+x,1/2+y +1/2-z,x,1/2+y +1/2-z,1/2+x,y +-x,1/2+z,1/2+y +1/2-x,z,1/2+y +1/2-x,1/2+z,y +-y,1/2+x,1/2+z +1/2-y,x,1/2+z +1/2-y,1/2+x,z +-z,1/2+y,1/2+x +1/2-z,y,1/2+x +1/2-z,1/2+y,x +x,1/2-y,1/2+z +1/2+x,-y,1/2+z +1/2+x,1/2-y,z +y,1/2-z,1/2+x +1/2+y,-z,1/2+x +1/2+y,1/2-z,x +z,1/2-x,1/2+y +1/2+z,-x,1/2+y +1/2+z,1/2-x,y +x,1/2-z,1/2+y +1/2+x,-z,1/2+y +1/2+x,1/2-z,y +y,1/2-x,1/2+z +1/2+y,-x,1/2+z +1/2+y,1/2-x,z +z,1/2-y,1/2+x +1/2+z,-y,1/2+x +1/2+z,1/2-y,x +x,1/2+y,1/2-z +1/2+x,y,1/2-z +1/2+x,1/2+y,-z +y,1/2+z,1/2-x +1/2+y,z,1/2-x +1/2+y,1/2+z,-x +z,1/2+x,1/2-y +1/2+z,x,1/2-y +1/2+z,1/2+x,-y +x,1/2+z,1/2-y +1/2+x,z,1/2-y +1/2+x,1/2+z,-y +y,1/2+x,1/2-z +1/2+y,x,1/2-z +1/2+y,1/2+x,-z +z,1/2+y,1/2-x +1/2+z,y,1/2-x +1/2+z,1/2+y,-x +loop_ +_atom_site_label +_atom_site_type_symbol +_atom_site_symmetry_multiplicity +_atom_site_Wyckoff_symbol +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +_atom_site_occupancy +_atom_site_attached_hydrogens +_atom_site_calc_flag +Na1 Na1+ 4 a 0. 0. 0. 1. 0 d +Cl1 Cl1- 4 b 0.5 0.5 0.5 1. 0 d +loop_ +_atom_type_symbol +_atom_type_oxidation_number +Na1+ 1.000 +Cl1- -1.000 diff --git a/notebooks/cgcnn-sample/1000050.cif b/notebooks/cgcnn-sample/1000050.cif new file mode 100644 index 000000000..c6115787b --- /dev/null +++ b/notebooks/cgcnn-sample/1000050.cif @@ -0,0 +1,113 @@ +#------------------------------------------------------------------------------ +#$Date: 2015-01-27 21:58:39 +0200 (Tue, 27 Jan 2015) $ +#$Revision: 130149 $ +#$URL: svn://www.crystallography.net/cod/cif/1/00/00/1000050.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/ +# +# All data on this site have been placed in the public domain by the +# contributors. +# +data_1000050 +loop_ +_publ_author_name +'Will, G' +_publ_section_title +; +Energiedispersion und Synchrotronstrahlung: Eine neue Methode und eine +neue Strahlenquelle fuer die Roentgenbeugung +; +_journal_coden_ASTM FMRLAL +_journal_name_full 'Fortschritte der Mineralogie' +_journal_page_first 31 +_journal_page_last 94 +_journal_volume 59 +_journal_year 1981 +_chemical_formula_structural 'K Cl' +_chemical_formula_sum 'Cl K' +_chemical_name_systematic 'Potassium chloride' +_space_group_IT_number 221 +_symmetry_cell_setting cubic +_symmetry_Int_Tables_number 221 +_symmetry_space_group_name_Hall '-P 4 2 3' +_symmetry_space_group_name_H-M 'P m -3 m' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_formula_units_Z 1 +_cell_length_a 3.634(4) +_cell_length_b 3.634(4) +_cell_length_c 3.634(4) +_cell_volume 48.0 +_refine_ls_R_factor_all 0.037 +_cod_database_code 1000050 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +y,z,x +z,x,y +x,z,y +y,x,z +z,y,x +x,-y,-z +y,-z,-x +z,-x,-y +x,-z,-y +y,-x,-z +z,-y,-x +-x,y,-z +-y,z,-x +-z,x,-y +-x,z,-y +-y,x,-z +-z,y,-x +-x,-y,z +-y,-z,x +-z,-x,y +-x,-z,y +-y,-x,z +-z,-y,x +-x,-y,-z +-y,-z,-x +-z,-x,-y +-x,-z,-y +-y,-x,-z +-z,-y,-x +-x,y,z +-y,z,x +-z,x,y +-x,z,y +-y,x,z +-z,y,x +x,-y,z +y,-z,x +z,-x,y +x,-z,y +y,-x,z +z,-y,x +x,y,-z +y,z,-x +z,x,-y +x,z,-y +y,x,-z +z,y,-x +loop_ +_atom_site_label +_atom_site_type_symbol +_atom_site_symmetry_multiplicity +_atom_site_Wyckoff_symbol +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +_atom_site_occupancy +_atom_site_attached_hydrogens +_atom_site_calc_flag +K1 K1+ 1 a 0. 0. 0. 1. 0 d +Cl1 Cl1- 1 b 0.5 0.5 0.5 1. 0 d +loop_ +_atom_type_symbol +_atom_type_oxidation_number +K1+ 1.000 +Cl1- -1.000 diff --git a/notebooks/cgcnn-sample/1101051.cif b/notebooks/cgcnn-sample/1101051.cif new file mode 100644 index 000000000..43a16e2c5 --- /dev/null +++ b/notebooks/cgcnn-sample/1101051.cif @@ -0,0 +1,85 @@ +#------------------------------------------------------------------------------ +#$Date: 2017-09-12 14:39:55 +0300 (Tue, 12 Sep 2017) $ +#$Revision: 200785 $ +#$URL: file:///home/coder/svn-repositories/cod/cif/1/10/10/1101051.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/ +# +# All data on this site have been placed in the public domain by the +# contributors. +# +data_1101051 +loop_ +_publ_author_name +'Kisi, E. H.' +'Elcombe, M. M.' +_publ_section_title +; + u parameters for the wurtzite structure of ZnS and ZnO using powder + neutron diffraction +; +_journal_coden_ASTM ACSCEE +_journal_issue 12 +_journal_name_full +; + Acta Crystallographica, Section C: Crystal Structure Communications +; +_journal_page_first 1867 +_journal_page_last 1870 +_journal_paper_doi 10.1107/s0108270189004269 +_journal_volume 45 +_journal_year 1989 +_chemical_formula_structural 'Zn S' +_chemical_formula_sum 'S Zn' +_chemical_name_mineral Wurtzite +_chemical_name_systematic 'Zinc sulfide' +_space_group_IT_number 186 +_symmetry_cell_setting hexagonal +_symmetry_Int_Tables_number 186 +_symmetry_space_group_name_Hall 'P 6c -2c' +_symmetry_space_group_name_H-M 'P 63 m c' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 120 +_cell_formula_units_Z 2 +_cell_length_a 3.8227(1) +_cell_length_b 3.8227(1) +_cell_length_c 6.2607(1) +_cell_volume 79.2 +_exptl_crystal_density_meas 4.09 +_refine_ls_R_factor_all 0.0575 +_cod_database_code 1101051 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +-y,x-y,z +y-x,-x,z +-y,-x,z +y-x,y,z +x,x-y,z +-x,-y,1/2+z +y,y-x,1/2+z +x-y,x,1/2+z +y,x,1/2+z +x-y,-y,1/2+z +-x,y-x,1/2+z +loop_ +_atom_site_label +_atom_site_type_symbol +_atom_site_symmetry_multiplicity +_atom_site_Wyckoff_symbol +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +_atom_site_occupancy +_atom_site_attached_hydrogens +_atom_site_calc_flag +Zn1 Zn2+ 2 b 0.3333 0.6667 0. 1. 0 d +S1 S2- 2 b 0.3333 0.6667 0.3748(2) 1. 0 d +loop_ +_atom_type_symbol +_atom_type_oxidation_number +Zn2+ 2.000 +S2- -2.000 diff --git a/notebooks/cgcnn-sample/1507756.cif b/notebooks/cgcnn-sample/1507756.cif new file mode 100644 index 000000000..5c079b2a4 --- /dev/null +++ b/notebooks/cgcnn-sample/1507756.cif @@ -0,0 +1,80 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-02-14 06:40:26 +0200 (Sun, 14 Feb 2016) $ +#$Revision: 176432 $ +#$URL: svn://www.crystallography.net/cod/cif/1/50/77/1507756.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/ +# +# All data on this site have been placed in the public domain by the +# contributors. +# +data_1507756 +loop_ +_publ_author_name +'Natheer B. Mahmood' +'Emad K. Al-Shakarchi' +_publ_section_title +; + Three Techniques Used to Produce BaTiO3 Fine Powder +; +_journal_name_full 'Journal of Modern Physics' +_journal_page_first 1420 +_journal_page_last 1428 +_journal_paper_doi 10.4236/jmp.2011.211175 +_journal_volume 2 +_journal_year 2011 +_chemical_formula_structural BaTiO3 +_chemical_formula_sum 'Ba O3 Ti' +_space_group_IT_number 99 +_symmetry_Int_Tables_number 99 +_symmetry_space_group_name_Hall 'P 4 -2' +_symmetry_space_group_name_H-M 'P 4 m m' +_audit_creation_date 2012-06-19 +_audit_creation_method 'Journal of Modern Physics,' +_audit_update_record 2012-06-19 +_cell_angle_alpha 90.000 +_cell_angle_beta 90.000 +_cell_angle_gamma 90.000 +_cell_formula_units_Z 1 +_cell_length_a 3.9999 +_cell_length_b 3.9999 +_cell_length_c 4.0170 +_cell_volume 64.269 +_cod_data_source_file BT.cif +_cod_data_source_block BaTiO3 +_cod_original_cell_volume 64.3 +_cod_original_sg_symbol_Hall P_4_-2 +_cod_database_code 1507756 +loop_ +_symmetry_equiv_pos_site_id +_symmetry_equiv_pos_as_xyz +1 x,y,z +2 -y,x,z +3 -x,-y,z +4 y,-x,z +5 -x,y,z +6 x,-y,z +7 -y,-x,z +8 y,x,z +loop_ +_atom_site_label +_atom_site_type_symbol +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +_atom_site_occupancy +_atom_site_symmetry_multiplicity +_atom_site_Wyckoff_symbol +_atom_site_calc_flag +ba Ba 0.0000 0.0000 0.0000 1.000 1 a d +ti Ti 0.5000 0.5000 0.4820 1.000 1 b d +o O 0.5000 0.5000 0.0160 1.000 1 b d +o O 0.5000 0.0000 0.5150 1.000 2 c d +loop_ +_atom_type_symbol +_atom_type_radius_bond +Ba 1.200 +Ti 1.200 +O 1.200 diff --git a/notebooks/cgcnn-sample/7206075.cif b/notebooks/cgcnn-sample/7206075.cif new file mode 100644 index 000000000..fb163ff90 --- /dev/null +++ b/notebooks/cgcnn-sample/7206075.cif @@ -0,0 +1,119 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-03-26 17:23:40 +0200 (Sat, 26 Mar 2016) $ +#$Revision: 180391 $ +#$URL: svn://www.crystallography.net/cod/cif/7/20/60/7206075.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/ +# +# All data on this site have been placed in the public domain by the +# contributors. +# +data_7206075 +loop_ +_publ_author_name +'Rezaee, Masih' +'Mousavi Khoie, Seyyed Mohammad' +'Liu, Kun Hua' +_publ_section_title +; + The role of brookite in mechanical activation of anatase-to-rutile + transformation of nanocrystalline TiO2: An XRD and Raman spectroscopy + investigation +; +_journal_issue 16 +_journal_name_full CrystEngComm +_journal_page_first 5055 +_journal_paper_doi 10.1039/c1ce05185g +_journal_volume 13 +_journal_year 2011 +_chemical_formula_structural 'Ti O2' +_chemical_formula_sum 'O2 Ti' +_chemical_name_mineral Anatase +_chemical_name_systematic 'Titanium oxide' +_space_group_IT_number 141 +_symmetry_cell_setting tetragonal +_symmetry_Int_Tables_number 141 +_symmetry_space_group_name_Hall 'I 4bw 2bw -1bw' +_symmetry_space_group_name_H-M 'I 41/a m d :1' +_audit_update_record +; +2011-02-06 # Formatted by publCIF +; +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_formula_units_Z 4 +_cell_length_a 3.7850 +_cell_length_b 3.7850 +_cell_length_c 9.5196 +_cell_measurement_temperature 298 +_cell_volume 136.380 +_computing_cell_refinement MAUD +_computing_data_collection X'Pert +_computing_data_reduction MAUD +_computing_publication_material publCIF +_computing_structure_refinement MAUD +_computing_structure_solution MAUD +_diffrn_measurement_device_type 'GBC MMA X-ray diffractometer' +_diffrn_radiation_source 'Cu K\a' +_diffrn_radiation_type 'Cu K\a' +_diffrn_radiation_wavelength 1.541874 +_diffrn_reflns_theta_max 50 +_cod_data_source_file 400a_anatase_.txt +_cod_data_source_block 11A +_cod_original_sg_symbol_H-M 'I 41/a m d S' +_cod_database_code 7206075 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +-x,-y,z +x,1/2+y,1/4-z +-x,1/2-y,1/4-z +-x,y,z +x,-y,z +-x,1/2+y,1/4-z +x,1/2-y,1/4-z +y,x,-z +-y,-x,-z +y,1/2+x,1/4+z +-y,1/2-x,1/4+z +-y,x,-z +y,-x,-z +-y,1/2+x,1/4+z +y,1/2-x,1/4+z +1/2+x,1/2+y,1/2+z +1/2-x,1/2-y,1/2+z +1/2+x,y,3/4-z +1/2-x,-y,3/4-z +1/2-x,1/2+y,1/2+z +1/2+x,1/2-y,1/2+z +1/2-x,y,3/4-z +1/2+x,-y,3/4-z +1/2+y,1/2+x,1/2-z +1/2-y,1/2-x,1/2-z +1/2+y,x,3/4+z +1/2-y,-x,3/4+z +1/2-y,1/2+x,1/2-z +1/2+y,1/2-x,1/2-z +1/2-y,x,3/4+z +1/2+y,-x,3/4+z +loop_ +_atom_site_label +_atom_site_type_symbol +_atom_site_symmetry_multiplicity +_atom_site_Wyckoff_symbol +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +_atom_site_occupancy +_atom_site_attached_hydrogens +_atom_site_calc_flag +Ti1 Ti4+ 4 a 0. 0. 0. 1. 0 d +O1 O2- 8 e 0. 0. 0.21017 1. 0 d +loop_ +_atom_type_symbol +_atom_type_oxidation_number +Ti4+ 4.000 +O2- -2.000 diff --git a/notebooks/cgcnn-sample/9000046.cif b/notebooks/cgcnn-sample/9000046.cif new file mode 100644 index 000000000..79781d477 --- /dev/null +++ b/notebooks/cgcnn-sample/9000046.cif @@ -0,0 +1,84 @@ +#------------------------------------------------------------------------------ +#$Date: 2013-05-05 17:21:46 +0300 (Sun, 05 May 2013) $ +#$Revision: 85285 $ +#$URL: svn://www.crystallography.net/cod/cif/9/00/00/9000046.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/. The original data for this entry +# were provided the American Mineralogist Crystal Structure Database, +# http://rruff.geo.arizona.edu/AMS/amcsd.php +# +# The file may be used within the scientific community so long as +# proper attribution is given to the journal article from which the +# data were obtained. +# +data_9000046 +loop_ +_publ_author_name +'Kukesh, J. S.' +'Pauling, L.' +_publ_section_title +; + The problem of the graphite structure +; +_journal_name_full 'American Mineralogist' +_journal_page_first 125 +_journal_page_last 125 +_journal_volume 35 +_journal_year 1950 +_chemical_formula_sum C +_chemical_name_common Graphite +_chemical_name_mineral Graphite +_space_group_IT_number 69 +_symmetry_space_group_name_Hall '-F 2 2' +_symmetry_space_group_name_H-M 'F m m m' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 2.456 +_cell_length_b 4.254 +_cell_length_c 6.696 +_cell_volume 69.959 +_exptl_crystal_density_diffrn 2.281 +_cod_database_code 9000046 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +x,1/2+y,1/2+z +1/2+x,y,1/2+z +1/2+x,1/2+y,z +x,-y,z +x,1/2-y,1/2+z +1/2+x,-y,1/2+z +1/2+x,1/2-y,z +-x,y,-z +-x,1/2+y,1/2-z +1/2-x,y,1/2-z +1/2-x,1/2+y,-z +-x,y,z +-x,1/2+y,1/2+z +1/2-x,y,1/2+z +1/2-x,1/2+y,z +x,-y,-z +x,1/2-y,1/2-z +1/2+x,-y,1/2-z +1/2+x,1/2-y,-z +x,y,-z +x,1/2+y,1/2-z +1/2+x,y,1/2-z +1/2+x,1/2+y,-z +-x,-y,z +-x,1/2-y,1/2+z +1/2-x,-y,1/2+z +1/2-x,1/2-y,z +-x,-y,-z +-x,1/2-y,1/2-z +1/2-x,-y,1/2-z +1/2-x,1/2-y,-z +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +C 0.00000 0.16667 0.00000 diff --git a/notebooks/cgcnn-sample/9009743.cif b/notebooks/cgcnn-sample/9009743.cif new file mode 100644 index 000000000..b9fe05cef --- /dev/null +++ b/notebooks/cgcnn-sample/9009743.cif @@ -0,0 +1,103 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-02-16 14:49:47 +0200 (Tue, 16 Feb 2016) $ +#$Revision: 176465 $ +#$URL: svn://www.crystallography.net/cod/cif/9/00/97/9009743.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/. The original data for this entry +# were provided the American Mineralogist Crystal Structure Database, +# http://rruff.geo.arizona.edu/AMS/amcsd.php +# +# The file may be used within the scientific community so long as +# proper attribution is given to the journal article from which the +# data were obtained. +# +data_9009743 +loop_ +_publ_author_name +'Ahtee, M.' +_publ_section_title +; + Lattice constants of some binary alkali halide solid solutions. +; +_journal_name_full +'Annales Academiae Scientiarum Fennicae Series A6: Physica' +_journal_page_first 1 +_journal_page_last 11 +_journal_paper_doi 10.1177/000271625731300103 +_journal_volume 313 +_journal_year 1969 +_chemical_formula_structural CsCl +_chemical_formula_sum 'Cl Cs' +_space_group_IT_number 221 +_symmetry_space_group_name_Hall '-P 4 2 3' +_symmetry_space_group_name_H-M 'P m -3 m' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 4.1150 +_cell_length_b 4.1150 +_cell_length_c 4.1150 +_cell_volume 69.680 +_exptl_crystal_density_diffrn 4.012 +_cod_original_sg_symbol_H-M 'P m 3 m' +_cod_original_formula_sum 'Cs Cl' +_cod_database_code 9009743 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +z,-x,y +-y,z,-x +x,-y,z +-z,x,-y +y,-z,x +-x,y,-z +x,-z,-y +-z,y,x +y,-x,-z +-x,z,y +z,-y,-x +-y,x,z +x,z,y +-z,-y,-x +y,x,z +-x,-z,-y +z,y,x +-y,-x,-z +z,x,-y +-y,-z,x +x,y,-z +-z,-x,y +y,z,-x +-x,-y,z +-z,x,y +y,-z,-x +-x,y,z +z,-x,-y +-y,z,x +x,-y,-z +-x,z,-y +z,-y,x +-y,x,-z +x,-z,y +-z,y,-x +y,-x,z +-x,-z,y +z,y,-x +-y,-x,z +x,z,-y +-z,-y,x +y,x,-z +-z,-x,-y +y,z,x +-x,-y,-z +z,x,y +-y,-z,-x +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +Cs 0.00000 0.00000 0.00000 +Cl 0.50000 0.50000 0.50000 diff --git a/notebooks/cgcnn-sample/9011050.cif b/notebooks/cgcnn-sample/9011050.cif new file mode 100644 index 000000000..07fc534bd --- /dev/null +++ b/notebooks/cgcnn-sample/9011050.cif @@ -0,0 +1,248 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-02-18 13:08:31 +0200 (Thu, 18 Feb 2016) $ +#$Revision: 176725 $ +#$URL: svn://www.crystallography.net/cod/cif/9/01/10/9011050.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/. The original data for this entry +# were provided the American Mineralogist Crystal Structure Database, +# http://rruff.geo.arizona.edu/AMS/amcsd.php +# +# The file may be used within the scientific community so long as +# proper attribution is given to the journal article from which the +# data were obtained. +# +data_9011050 +loop_ +_publ_author_name +'Cooper, A. S.' +_publ_section_title +; + Precise lattice constants of germanium, aluminum, gallium arsenide, uranium, + sulphur, quartz and sapphire + Locality: synthetic + Sample: at T = 24.6 C +; +_journal_name_full 'Acta Crystallographica' +_journal_page_first 578 +_journal_page_last 582 +_journal_paper_doi 10.1107/S0365110X62001474 +_journal_volume 15 +_journal_year 1962 +_chemical_formula_sum Ge +_chemical_name_mineral Germanium +_space_group_IT_number 227 +_symmetry_space_group_name_Hall 'F 4d 2 3 -1d' +_symmetry_space_group_name_H-M 'F d -3 m :1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 5.65754 +_cell_length_b 5.65754 +_cell_length_c 5.65754 +_cell_volume 181.085 +_diffrn_ambient_temperature 297.75 +_exptl_crystal_density_diffrn 5.327 +_cod_original_sg_symbol_H-M 'F d 3 m' +_cod_database_code 9011050 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +x,1/2+y,1/2+z +1/2+x,y,1/2+z +1/2+x,1/2+y,z +3/4+z,3/4-x,1/4+y +3/4+z,1/4-x,3/4+y +1/4+z,3/4-x,3/4+y +1/4+z,1/4-x,1/4+y +-y,1/2+z,1/2-x +-y,+z,-x +1/2-y,1/2+z,-x +1/2-y,+z,1/2-x +3/4+x,3/4-y,1/4+z +3/4+x,1/4-y,3/4+z +1/4+x,3/4-y,3/4+z +1/4+x,1/4-y,1/4+z +-z,1/2+x,1/2-y +-z,+x,-y +1/2-z,1/2+x,-y +1/2-z,+x,1/2-y +3/4+y,3/4-z,1/4+x +3/4+y,1/4-z,3/4+x +1/4+y,3/4-z,3/4+x +1/4+y,1/4-z,1/4+x +-x,1/2+y,1/2-z +-x,+y,-z +1/2-x,1/2+y,-z +1/2-x,+y,1/2-z +1/2+x,-z,1/2-y +1/2+x,1/2-z,-y ++x,-z,-y ++x,1/2-z,1/2-y +3/4-z,3/4+y,1/4+x +3/4-z,1/4+y,3/4+x +1/4-z,3/4+y,3/4+x +1/4-z,1/4+y,1/4+x +1/2+y,-x,1/2-z +1/2+y,1/2-x,-z ++y,-x,-z ++y,1/2-x,1/2-z +3/4-x,3/4+z,1/4+y +3/4-x,1/4+z,3/4+y +1/4-x,3/4+z,3/4+y +1/4-x,1/4+z,1/4+y +1/2+z,-y,1/2-x +1/2+z,1/2-y,-x ++z,-y,-x ++z,1/2-y,1/2-x +3/4-y,3/4+x,1/4+z +3/4-y,1/4+x,3/4+z +1/4-y,3/4+x,3/4+z +1/4-y,1/4+x,1/4+z +x,1/2+z,1/2+y +x,+z,+y +1/2+x,1/2+z,+y +1/2+x,+z,1/2+y +1/4-z,3/4-y,3/4-x +1/4-z,1/4-y,1/4-x +3/4-z,3/4-y,1/4-x +3/4-z,1/4-y,3/4-x +y,1/2+x,1/2+z +y,+x,+z +1/2+y,1/2+x,+z +1/2+y,+x,1/2+z +1/4-x,3/4-z,3/4-y +1/4-x,1/4-z,1/4-y +3/4-x,3/4-z,1/4-y +3/4-x,1/4-z,3/4-y +z,1/2+y,1/2+x +z,+y,+x +1/2+z,1/2+y,+x +1/2+z,+y,1/2+x +1/4-y,3/4-x,3/4-z +1/4-y,1/4-x,1/4-z +3/4-y,3/4-x,1/4-z +3/4-y,1/4-x,3/4-z +3/4+z,1/4+x,3/4-y +3/4+z,3/4+x,1/4-y +1/4+z,1/4+x,1/4-y +1/4+z,3/4+x,3/4-y +-y,1/2-z,1/2+x +-y,-z,+x +1/2-y,1/2-z,+x +1/2-y,-z,1/2+x +3/4+x,1/4+y,3/4-z +3/4+x,3/4+y,1/4-z +1/4+x,1/4+y,1/4-z +1/4+x,3/4+y,3/4-z +-z,1/2-x,1/2+y +-z,-x,+y +1/2-z,1/2-x,+y +1/2-z,-x,1/2+y +3/4+y,1/4+z,3/4-x +3/4+y,3/4+z,1/4-x +1/4+y,1/4+z,1/4-x +1/4+y,3/4+z,3/4-x +-x,1/2-y,1/2+z +-x,-y,+z +1/2-x,1/2-y,+z +1/2-x,-y,1/2+z +1/4-z,3/4+x,3/4+y +1/4-z,1/4+x,1/4+y +3/4-z,3/4+x,1/4+y +3/4-z,1/4+x,3/4+y +y,-z,-x +y,1/2-z,1/2-x +1/2+y,-z,1/2-x +1/2+y,1/2-z,-x +1/4-x,3/4+y,3/4+z +1/4-x,1/4+y,1/4+z +3/4-x,3/4+y,1/4+z +3/4-x,1/4+y,3/4+z +z,-x,-y +z,1/2-x,1/2-y +1/2+z,-x,1/2-y +1/2+z,1/2-x,-y +1/4-y,3/4+z,3/4+x +1/4-y,1/4+z,1/4+x +3/4-y,3/4+z,1/4+x +3/4-y,1/4+z,3/4+x +x,-y,-z +x,1/2-y,1/2-z +1/2+x,-y,1/2-z +1/2+x,1/2-y,-z +1/2-x,1/2+z,-y +1/2-x,+z,1/2-y +-x,1/2+z,1/2-y +-x,+z,-y +1/4+z,3/4-y,3/4+x +1/4+z,1/4-y,1/4+x +3/4+z,3/4-y,1/4+x +3/4+z,1/4-y,3/4+x +1/2-y,1/2+x,-z +1/2-y,+x,1/2-z +-y,1/2+x,1/2-z +-y,+x,-z +1/4+x,3/4-z,3/4+y +1/4+x,1/4-z,1/4+y +3/4+x,3/4-z,1/4+y +3/4+x,1/4-z,3/4+y +1/2-z,1/2+y,-x +1/2-z,+y,1/2-x +-z,1/2+y,1/2-x +-z,+y,-x +1/4+y,3/4-x,3/4+z +1/4+y,1/4-x,1/4+z +3/4+y,3/4-x,1/4+z +3/4+y,1/4-x,3/4+z +-x,-z,y +-x,1/2-z,1/2+y +1/2-x,-z,1/2+y +1/2-x,1/2-z,y +3/4+z,3/4+y,1/4-x +3/4+z,1/4+y,3/4-x +1/4+z,3/4+y,3/4-x +1/4+z,1/4+y,1/4-x +-y,-x,z +-y,1/2-x,1/2+z +1/2-y,-x,1/2+z +1/2-y,1/2-x,z +3/4+x,3/4+z,1/4-y +3/4+x,1/4+z,3/4-y +1/4+x,3/4+z,3/4-y +1/4+x,1/4+z,1/4-y +-z,-y,x +-z,1/2-y,1/2+x +1/2-z,-y,1/2+x +1/2-z,1/2-y,x +3/4+y,3/4+x,1/4-z +3/4+y,1/4+x,3/4-z +1/4+y,3/4+x,3/4-z +1/4+y,1/4+x,1/4-z +1/4-z,1/4-x,1/4-y +1/4-z,3/4-x,3/4-y +3/4-z,1/4-x,3/4-y +3/4-z,3/4-x,1/4-y +y,z,x +y,1/2+z,1/2+x +1/2+y,z,1/2+x +1/2+y,1/2+z,x +1/4-x,1/4-y,1/4-z +1/4-x,3/4-y,3/4-z +3/4-x,1/4-y,3/4-z +3/4-x,3/4-y,1/4-z +z,x,y +z,1/2+x,1/2+y +1/2+z,x,1/2+y +1/2+z,1/2+x,y +1/4-y,1/4-z,1/4-x +1/4-y,3/4-z,3/4-x +3/4-y,1/4-z,3/4-x +3/4-y,3/4-z,1/4-x +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +Ge 0.00000 0.00000 0.00000 diff --git a/notebooks/cgcnn-sample/9011998.cif b/notebooks/cgcnn-sample/9011998.cif new file mode 100644 index 000000000..2787265a4 --- /dev/null +++ b/notebooks/cgcnn-sample/9011998.cif @@ -0,0 +1,250 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-02-18 13:08:31 +0200 (Thu, 18 Feb 2016) $ +#$Revision: 176725 $ +#$URL: svn://www.crystallography.net/cod/cif/9/01/19/9011998.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/. The original data for this entry +# were provided the American Mineralogist Crystal Structure Database, +# http://rruff.geo.arizona.edu/AMS/amcsd.php +# +# The file may be used within the scientific community so long as +# proper attribution is given to the journal article from which the +# data were obtained. +# +data_9011998 +loop_ +_publ_author_name +'Hom, T.' +'Kiszenick, W.' +'Post, B.' +_publ_section_title +; + Accurate lattice constants from multiple reflection mesurements II. + lattice constants of germanium, silicon and diamond + Locality: synthetic + Sample: at T = 25 C +; +_journal_name_full 'Journal of Applied Crystallography' +_journal_page_first 457 +_journal_page_last 458 +_journal_paper_doi 10.1107/S0021889875010965 +_journal_volume 8 +_journal_year 1975 +_chemical_formula_sum Si +_chemical_name_common Silicon +_space_group_IT_number 227 +_symmetry_space_group_name_Hall 'F 4d 2 3 -1d' +_symmetry_space_group_name_H-M 'F d -3 m :1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 5.430941 +_cell_length_b 5.430941 +_cell_length_c 5.430941 +_cell_volume 160.186 +_diffrn_ambient_temperature 298.15 +_exptl_crystal_density_diffrn 2.329 +_cod_original_sg_symbol_H-M 'F d 3 m' +_cod_database_code 9011998 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +x,1/2+y,1/2+z +1/2+x,y,1/2+z +1/2+x,1/2+y,z +3/4+z,3/4-x,1/4+y +3/4+z,1/4-x,3/4+y +1/4+z,3/4-x,3/4+y +1/4+z,1/4-x,1/4+y +-y,1/2+z,1/2-x +-y,+z,-x +1/2-y,1/2+z,-x +1/2-y,+z,1/2-x +3/4+x,3/4-y,1/4+z +3/4+x,1/4-y,3/4+z +1/4+x,3/4-y,3/4+z +1/4+x,1/4-y,1/4+z +-z,1/2+x,1/2-y +-z,+x,-y +1/2-z,1/2+x,-y +1/2-z,+x,1/2-y +3/4+y,3/4-z,1/4+x +3/4+y,1/4-z,3/4+x +1/4+y,3/4-z,3/4+x +1/4+y,1/4-z,1/4+x +-x,1/2+y,1/2-z +-x,+y,-z +1/2-x,1/2+y,-z +1/2-x,+y,1/2-z +1/2+x,-z,1/2-y +1/2+x,1/2-z,-y ++x,-z,-y ++x,1/2-z,1/2-y +3/4-z,3/4+y,1/4+x +3/4-z,1/4+y,3/4+x +1/4-z,3/4+y,3/4+x +1/4-z,1/4+y,1/4+x +1/2+y,-x,1/2-z +1/2+y,1/2-x,-z ++y,-x,-z ++y,1/2-x,1/2-z +3/4-x,3/4+z,1/4+y +3/4-x,1/4+z,3/4+y +1/4-x,3/4+z,3/4+y +1/4-x,1/4+z,1/4+y +1/2+z,-y,1/2-x +1/2+z,1/2-y,-x ++z,-y,-x ++z,1/2-y,1/2-x +3/4-y,3/4+x,1/4+z +3/4-y,1/4+x,3/4+z +1/4-y,3/4+x,3/4+z +1/4-y,1/4+x,1/4+z +x,1/2+z,1/2+y +x,+z,+y +1/2+x,1/2+z,+y +1/2+x,+z,1/2+y +1/4-z,3/4-y,3/4-x +1/4-z,1/4-y,1/4-x +3/4-z,3/4-y,1/4-x +3/4-z,1/4-y,3/4-x +y,1/2+x,1/2+z +y,+x,+z +1/2+y,1/2+x,+z +1/2+y,+x,1/2+z +1/4-x,3/4-z,3/4-y +1/4-x,1/4-z,1/4-y +3/4-x,3/4-z,1/4-y +3/4-x,1/4-z,3/4-y +z,1/2+y,1/2+x +z,+y,+x +1/2+z,1/2+y,+x +1/2+z,+y,1/2+x +1/4-y,3/4-x,3/4-z +1/4-y,1/4-x,1/4-z +3/4-y,3/4-x,1/4-z +3/4-y,1/4-x,3/4-z +3/4+z,1/4+x,3/4-y +3/4+z,3/4+x,1/4-y +1/4+z,1/4+x,1/4-y +1/4+z,3/4+x,3/4-y +-y,1/2-z,1/2+x +-y,-z,+x +1/2-y,1/2-z,+x +1/2-y,-z,1/2+x +3/4+x,1/4+y,3/4-z +3/4+x,3/4+y,1/4-z +1/4+x,1/4+y,1/4-z +1/4+x,3/4+y,3/4-z +-z,1/2-x,1/2+y +-z,-x,+y +1/2-z,1/2-x,+y +1/2-z,-x,1/2+y +3/4+y,1/4+z,3/4-x +3/4+y,3/4+z,1/4-x +1/4+y,1/4+z,1/4-x +1/4+y,3/4+z,3/4-x +-x,1/2-y,1/2+z +-x,-y,+z +1/2-x,1/2-y,+z +1/2-x,-y,1/2+z +1/4-z,3/4+x,3/4+y +1/4-z,1/4+x,1/4+y +3/4-z,3/4+x,1/4+y +3/4-z,1/4+x,3/4+y +y,-z,-x +y,1/2-z,1/2-x +1/2+y,-z,1/2-x +1/2+y,1/2-z,-x +1/4-x,3/4+y,3/4+z +1/4-x,1/4+y,1/4+z +3/4-x,3/4+y,1/4+z +3/4-x,1/4+y,3/4+z +z,-x,-y +z,1/2-x,1/2-y +1/2+z,-x,1/2-y +1/2+z,1/2-x,-y +1/4-y,3/4+z,3/4+x +1/4-y,1/4+z,1/4+x +3/4-y,3/4+z,1/4+x +3/4-y,1/4+z,3/4+x +x,-y,-z +x,1/2-y,1/2-z +1/2+x,-y,1/2-z +1/2+x,1/2-y,-z +1/2-x,1/2+z,-y +1/2-x,+z,1/2-y +-x,1/2+z,1/2-y +-x,+z,-y +1/4+z,3/4-y,3/4+x +1/4+z,1/4-y,1/4+x +3/4+z,3/4-y,1/4+x +3/4+z,1/4-y,3/4+x +1/2-y,1/2+x,-z +1/2-y,+x,1/2-z +-y,1/2+x,1/2-z +-y,+x,-z +1/4+x,3/4-z,3/4+y +1/4+x,1/4-z,1/4+y +3/4+x,3/4-z,1/4+y +3/4+x,1/4-z,3/4+y +1/2-z,1/2+y,-x +1/2-z,+y,1/2-x +-z,1/2+y,1/2-x +-z,+y,-x +1/4+y,3/4-x,3/4+z +1/4+y,1/4-x,1/4+z +3/4+y,3/4-x,1/4+z +3/4+y,1/4-x,3/4+z +-x,-z,y +-x,1/2-z,1/2+y +1/2-x,-z,1/2+y +1/2-x,1/2-z,y +3/4+z,3/4+y,1/4-x +3/4+z,1/4+y,3/4-x +1/4+z,3/4+y,3/4-x +1/4+z,1/4+y,1/4-x +-y,-x,z +-y,1/2-x,1/2+z +1/2-y,-x,1/2+z +1/2-y,1/2-x,z +3/4+x,3/4+z,1/4-y +3/4+x,1/4+z,3/4-y +1/4+x,3/4+z,3/4-y +1/4+x,1/4+z,1/4-y +-z,-y,x +-z,1/2-y,1/2+x +1/2-z,-y,1/2+x +1/2-z,1/2-y,x +3/4+y,3/4+x,1/4-z +3/4+y,1/4+x,3/4-z +1/4+y,3/4+x,3/4-z +1/4+y,1/4+x,1/4-z +1/4-z,1/4-x,1/4-y +1/4-z,3/4-x,3/4-y +3/4-z,1/4-x,3/4-y +3/4-z,3/4-x,1/4-y +y,z,x +y,1/2+z,1/2+x +1/2+y,z,1/2+x +1/2+y,1/2+z,x +1/4-x,1/4-y,1/4-z +1/4-x,3/4-y,3/4-z +3/4-x,1/4-y,3/4-z +3/4-x,3/4-y,1/4-z +z,x,y +z,1/2+x,1/2+y +1/2+z,x,1/2+y +1/2+z,1/2+x,y +1/4-y,1/4-z,1/4-x +1/4-y,3/4-z,3/4-x +3/4-y,1/4-z,3/4-x +3/4-y,3/4-z,1/4-x +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +Si 0.00000 0.00000 0.00000 diff --git a/notebooks/cgcnn-sample/9012304.cif b/notebooks/cgcnn-sample/9012304.cif new file mode 100644 index 000000000..58f862294 --- /dev/null +++ b/notebooks/cgcnn-sample/9012304.cif @@ -0,0 +1,248 @@ +#------------------------------------------------------------------------------ +#$Date: 2016-02-18 13:08:31 +0200 (Thu, 18 Feb 2016) $ +#$Revision: 176725 $ +#$URL: svn://www.crystallography.net/cod/cif/9/01/23/9012304.cif $ +#------------------------------------------------------------------------------ +# +# This file is available in the Crystallography Open Database (COD), +# http://www.crystallography.net/. The original data for this entry +# were provided the American Mineralogist Crystal Structure Database, +# http://rruff.geo.arizona.edu/AMS/amcsd.php +# +# The file may be used within the scientific community so long as +# proper attribution is given to the journal article from which the +# data were obtained. +# +data_9012304 +loop_ +_publ_author_name +'Straumanis, M. E.' +'Aka, E. Z.' +_publ_section_title +;Precision determination of lattice parameter, coefficient of thermal + expansion and atomic weight of carbon in diamond Locality: Belgian Congo + Sample: at T = 10 C Note: Diamond #2 +; +_journal_name_full 'Journal of the American Chemical Society' +_journal_page_first 5643 +_journal_page_last 5646 +_journal_paper_doi 10.1021/ja01156a043 +_journal_volume 73 +_journal_year 1951 +_chemical_formula_sum C +_chemical_name_common Diamond +_chemical_name_mineral Diamond +_space_group_IT_number 227 +_symmetry_space_group_name_Hall 'F 4d 2 3 -1d' +_symmetry_space_group_name_H-M 'F d -3 m :1' +_cell_angle_alpha 90 +_cell_angle_beta 90 +_cell_angle_gamma 90 +_cell_length_a 3.56670 +_cell_length_b 3.56670 +_cell_length_c 3.56670 +_cell_volume 45.373 +_diffrn_ambient_temperature 283.15 +_exptl_crystal_density_diffrn 3.517 +_cod_original_sg_symbol_H-M 'F d 3 m' +_cod_database_code 9012304 +loop_ +_symmetry_equiv_pos_as_xyz +x,y,z +x,1/2+y,1/2+z +1/2+x,y,1/2+z +1/2+x,1/2+y,z +3/4+z,3/4-x,1/4+y +3/4+z,1/4-x,3/4+y +1/4+z,3/4-x,3/4+y +1/4+z,1/4-x,1/4+y +-y,1/2+z,1/2-x +-y,+z,-x +1/2-y,1/2+z,-x +1/2-y,+z,1/2-x +3/4+x,3/4-y,1/4+z +3/4+x,1/4-y,3/4+z +1/4+x,3/4-y,3/4+z +1/4+x,1/4-y,1/4+z +-z,1/2+x,1/2-y +-z,+x,-y +1/2-z,1/2+x,-y +1/2-z,+x,1/2-y +3/4+y,3/4-z,1/4+x +3/4+y,1/4-z,3/4+x +1/4+y,3/4-z,3/4+x +1/4+y,1/4-z,1/4+x +-x,1/2+y,1/2-z +-x,+y,-z +1/2-x,1/2+y,-z +1/2-x,+y,1/2-z +1/2+x,-z,1/2-y +1/2+x,1/2-z,-y ++x,-z,-y ++x,1/2-z,1/2-y +3/4-z,3/4+y,1/4+x +3/4-z,1/4+y,3/4+x +1/4-z,3/4+y,3/4+x +1/4-z,1/4+y,1/4+x +1/2+y,-x,1/2-z +1/2+y,1/2-x,-z ++y,-x,-z ++y,1/2-x,1/2-z +3/4-x,3/4+z,1/4+y +3/4-x,1/4+z,3/4+y +1/4-x,3/4+z,3/4+y +1/4-x,1/4+z,1/4+y +1/2+z,-y,1/2-x +1/2+z,1/2-y,-x ++z,-y,-x ++z,1/2-y,1/2-x +3/4-y,3/4+x,1/4+z +3/4-y,1/4+x,3/4+z +1/4-y,3/4+x,3/4+z +1/4-y,1/4+x,1/4+z +x,1/2+z,1/2+y +x,+z,+y +1/2+x,1/2+z,+y +1/2+x,+z,1/2+y +1/4-z,3/4-y,3/4-x +1/4-z,1/4-y,1/4-x +3/4-z,3/4-y,1/4-x +3/4-z,1/4-y,3/4-x +y,1/2+x,1/2+z +y,+x,+z +1/2+y,1/2+x,+z +1/2+y,+x,1/2+z +1/4-x,3/4-z,3/4-y +1/4-x,1/4-z,1/4-y +3/4-x,3/4-z,1/4-y +3/4-x,1/4-z,3/4-y +z,1/2+y,1/2+x +z,+y,+x +1/2+z,1/2+y,+x +1/2+z,+y,1/2+x +1/4-y,3/4-x,3/4-z +1/4-y,1/4-x,1/4-z +3/4-y,3/4-x,1/4-z +3/4-y,1/4-x,3/4-z +3/4+z,1/4+x,3/4-y +3/4+z,3/4+x,1/4-y +1/4+z,1/4+x,1/4-y +1/4+z,3/4+x,3/4-y +-y,1/2-z,1/2+x +-y,-z,+x +1/2-y,1/2-z,+x +1/2-y,-z,1/2+x +3/4+x,1/4+y,3/4-z +3/4+x,3/4+y,1/4-z +1/4+x,1/4+y,1/4-z +1/4+x,3/4+y,3/4-z +-z,1/2-x,1/2+y +-z,-x,+y +1/2-z,1/2-x,+y +1/2-z,-x,1/2+y +3/4+y,1/4+z,3/4-x +3/4+y,3/4+z,1/4-x +1/4+y,1/4+z,1/4-x +1/4+y,3/4+z,3/4-x +-x,1/2-y,1/2+z +-x,-y,+z +1/2-x,1/2-y,+z +1/2-x,-y,1/2+z +1/4-z,3/4+x,3/4+y +1/4-z,1/4+x,1/4+y +3/4-z,3/4+x,1/4+y +3/4-z,1/4+x,3/4+y +y,-z,-x +y,1/2-z,1/2-x +1/2+y,-z,1/2-x +1/2+y,1/2-z,-x +1/4-x,3/4+y,3/4+z +1/4-x,1/4+y,1/4+z +3/4-x,3/4+y,1/4+z +3/4-x,1/4+y,3/4+z +z,-x,-y +z,1/2-x,1/2-y +1/2+z,-x,1/2-y +1/2+z,1/2-x,-y +1/4-y,3/4+z,3/4+x +1/4-y,1/4+z,1/4+x +3/4-y,3/4+z,1/4+x +3/4-y,1/4+z,3/4+x +x,-y,-z +x,1/2-y,1/2-z +1/2+x,-y,1/2-z +1/2+x,1/2-y,-z +1/2-x,1/2+z,-y +1/2-x,+z,1/2-y +-x,1/2+z,1/2-y +-x,+z,-y +1/4+z,3/4-y,3/4+x +1/4+z,1/4-y,1/4+x +3/4+z,3/4-y,1/4+x +3/4+z,1/4-y,3/4+x +1/2-y,1/2+x,-z +1/2-y,+x,1/2-z +-y,1/2+x,1/2-z +-y,+x,-z +1/4+x,3/4-z,3/4+y +1/4+x,1/4-z,1/4+y +3/4+x,3/4-z,1/4+y +3/4+x,1/4-z,3/4+y +1/2-z,1/2+y,-x +1/2-z,+y,1/2-x +-z,1/2+y,1/2-x +-z,+y,-x +1/4+y,3/4-x,3/4+z +1/4+y,1/4-x,1/4+z +3/4+y,3/4-x,1/4+z +3/4+y,1/4-x,3/4+z +-x,-z,y +-x,1/2-z,1/2+y +1/2-x,-z,1/2+y +1/2-x,1/2-z,y +3/4+z,3/4+y,1/4-x +3/4+z,1/4+y,3/4-x +1/4+z,3/4+y,3/4-x +1/4+z,1/4+y,1/4-x +-y,-x,z +-y,1/2-x,1/2+z +1/2-y,-x,1/2+z +1/2-y,1/2-x,z +3/4+x,3/4+z,1/4-y +3/4+x,1/4+z,3/4-y +1/4+x,3/4+z,3/4-y +1/4+x,1/4+z,1/4-y +-z,-y,x +-z,1/2-y,1/2+x +1/2-z,-y,1/2+x +1/2-z,1/2-y,x +3/4+y,3/4+x,1/4-z +3/4+y,1/4+x,3/4-z +1/4+y,3/4+x,3/4-z +1/4+y,1/4+x,1/4-z +1/4-z,1/4-x,1/4-y +1/4-z,3/4-x,3/4-y +3/4-z,1/4-x,3/4-y +3/4-z,3/4-x,1/4-y +y,z,x +y,1/2+z,1/2+x +1/2+y,z,1/2+x +1/2+y,1/2+z,x +1/4-x,1/4-y,1/4-z +1/4-x,3/4-y,3/4-z +3/4-x,1/4-y,3/4-z +3/4-x,3/4-y,1/4-z +z,x,y +z,1/2+x,1/2+y +1/2+z,x,1/2+y +1/2+z,1/2+x,y +1/4-y,1/4-z,1/4-x +1/4-y,3/4-z,3/4-x +3/4-y,1/4-z,3/4-x +3/4-y,3/4-z,1/4-x +loop_ +_atom_site_label +_atom_site_fract_x +_atom_site_fract_y +_atom_site_fract_z +C 0.00000 0.00000 0.00000 diff --git a/notebooks/cgcnn-sample/atom_init.json b/notebooks/cgcnn-sample/atom_init.json new file mode 100644 index 000000000..b8699f5a9 --- /dev/null +++ b/notebooks/cgcnn-sample/atom_init.json @@ -0,0 +1 @@ +{"1": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "2": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], "3": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "4": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 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, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], "5": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 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, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], "6": [0, 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, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 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, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], "7": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "8": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "9": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "10": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "11": [0, 1, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "12": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "13": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "14": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "15": [0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "16": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "17": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "18": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 1, 0, 0, 0], "19": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], "20": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0], "21": [0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "22": [0, 0, 0, 0, 1, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "23": [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "24": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "25": [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "26": [0, 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, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "27": [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "28": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "29": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], "30": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "31": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "32": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "33": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "34": [0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "35": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "36": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], "37": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "38": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0], "39": [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "40": [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "41": [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "42": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "43": [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "44": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "45": [0, 0, 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, 0, 0, 1, 0, 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, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "46": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "47": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "48": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "49": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "50": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "51": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "52": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "53": [0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], "54": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], "55": [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], "56": [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0], "57": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "58": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "59": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 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, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "60": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 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, 1, 0, 0, 0, 0], "61": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "62": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "63": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], "64": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "65": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "66": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "67": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "68": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "69": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 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, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "70": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], "71": [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "72": [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "73": [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "74": [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "75": [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "76": [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "77": [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "78": [0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "79": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 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, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], "80": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "81": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0], "82": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0], "83": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0], "84": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "85": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "86": [0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "87": [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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "88": [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, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0], "89": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 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, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "90": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "91": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], "92": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], "93": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "94": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "95": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "96": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], "97": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "98": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "99": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "100": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]} \ No newline at end of file diff --git a/notebooks/cgcnn-sample/id_prop.csv b/notebooks/cgcnn-sample/id_prop.csv new file mode 100644 index 000000000..7671f6c97 --- /dev/null +++ b/notebooks/cgcnn-sample/id_prop.csv @@ -0,0 +1,10 @@ +1000041,1 +1000050,0 +1101051,1 +1507756,0 +7206075,1 +9000046,0 +9009743,1 +9011050,0 +9011998,1 +9012304,0 diff --git a/requirements.txt b/requirements.txt index 98f2c7ee9..c9cade4ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,3 +39,4 @@ torchvision>=0.12.0 transformers>=4.22.0 typing_extensions>=3.7.4.3 wheel>=0.26 +pymatgen>=2022.11.7 diff --git a/src/gt4sd/algorithms/controlled_sampling/tests/test_paccmann_gp.py b/src/gt4sd/algorithms/controlled_sampling/tests/test_paccmann_gp.py index 929c5268a..bde206596 100644 --- a/src/gt4sd/algorithms/controlled_sampling/tests/test_paccmann_gp.py +++ b/src/gt4sd/algorithms/controlled_sampling/tests/test_paccmann_gp.py @@ -45,7 +45,7 @@ "number_of_steps": 8, "number_of_initial_points": 4, "number_of_optimization_rounds": 1, - "samples_for_evaluation": 5, + "samples_for_evaluation": 10, "maximum_number_of_sampling_steps": 4, } diff --git a/src/gt4sd/frameworks/cgcnn/__init__.py b/src/gt4sd/frameworks/cgcnn/__init__.py new file mode 100644 index 000000000..d3f9933dc --- /dev/null +++ b/src/gt4sd/frameworks/cgcnn/__init__.py @@ -0,0 +1,24 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""cgcnn - Module for Crystal Graph Convolutional Neural Networks.""" diff --git a/src/gt4sd/frameworks/cgcnn/data.py b/src/gt4sd/frameworks/cgcnn/data.py new file mode 100644 index 000000000..c547cc8ea --- /dev/null +++ b/src/gt4sd/frameworks/cgcnn/data.py @@ -0,0 +1,417 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""Data module.""" + +from __future__ import division, print_function + +import csv +import functools +import json +import logging +import os +import random +from typing import Any, Callable, List, Tuple, Union + +import numpy as np +import torch +from pymatgen.core.structure import Structure # type: ignore +from torch import LongTensor, Tensor +from torch.utils.data import DataLoader, Dataset +from torch.utils.data.dataloader import default_collate +from torch.utils.data.sampler import SubsetRandomSampler + +logger = logging.getLogger(__name__) +logger.addHandler(logging.NullHandler()) + + +def get_train_val_test_loader( + dataset: torch.utils.data.Dataset, + collate_fn: Callable[[List[Any]], Any] = default_collate, + batch_size: int = 64, + train_ratio: float = None, + val_ratio: float = 0.1, + test_ratio: float = 0.1, + return_test: bool = False, + num_workers: int = 1, + pin_memory: bool = False, + **kwargs, +) -> Union[ + Tuple[DataLoader[Any], DataLoader[Any], DataLoader[Any]], + Tuple[DataLoader[Any], DataLoader[Any]], +]: + """Utility function for dividing a dataset to train, val, test datasets. + + !!! The dataset needs to be shuffled before using the function !!! + + Args: + dataset: torch.utils.data.Dataset + The full dataset to be divided. + collate_fn: torch.utils.data.DataLoader. + batch_size: int. + train_ratio: float. + val_ratio: float. + test_ratio: float. + return_test: bool. + Whether to return the test dataset loader. If False, the last test_size + data will be hidden. + num_workers: int. + pin_memory: bool. + + Returns: + train_loader: torch.utils.data.DataLoader + DataLoader that random samples the training data. + val_loader: torch.utils.data.DataLoader + DataLoader that random samples the validation data. + (test_loader): torch.utils.data.DataLoader + DataLoader that random samples the test data, Returns if + return_test=True. + """ + total_size = len(dataset) # type: ignore + if kwargs["train_size"] is None: + if train_ratio is None: + assert val_ratio + test_ratio < 1 + train_ratio = 1 - val_ratio - test_ratio + logger.warning( + f"train_ratio is None, using 1 - val_ratio - " + f"test_ratio = {train_ratio} as training data." + ) + + else: + assert train_ratio + val_ratio + test_ratio <= 1 + indices = list(range(total_size)) + if kwargs["train_size"]: + train_size = kwargs["train_size"] + else: + train_size = int(train_ratio * total_size) # type: ignore + if kwargs["test_size"]: + test_size = kwargs["test_size"] + else: + test_size = int(test_ratio * total_size) + if kwargs["val_size"]: + valid_size = kwargs["val_size"] + else: + valid_size = int(val_ratio * total_size) + train_sampler = SubsetRandomSampler(indices[:train_size]) + val_sampler = SubsetRandomSampler(indices[-(valid_size + test_size) : -test_size]) + + train_loader = DataLoader( + dataset, + batch_size=batch_size, + sampler=train_sampler, + num_workers=num_workers, + collate_fn=collate_fn, + pin_memory=pin_memory, + ) + val_loader = DataLoader( + dataset, + batch_size=batch_size, + sampler=val_sampler, + num_workers=num_workers, + collate_fn=collate_fn, + pin_memory=pin_memory, + ) + if return_test: + + test_sampler = SubsetRandomSampler(indices[-test_size:]) + + test_loader = DataLoader( + dataset, + batch_size=batch_size, + sampler=test_sampler, + num_workers=num_workers, + collate_fn=collate_fn, + pin_memory=pin_memory, + ) + + return train_loader, val_loader, test_loader + else: + return train_loader, val_loader + + +def collate_pool( + dataset_list: List[Any], +) -> Tuple[Tuple[Tensor, Tensor, Tensor, List[LongTensor]], Tensor, List[Any]]: + """Collate a list of data and return a batch for predicting crystal properties. + + Args: + dataset_list: list of tuples for each data point. + (atom_fea, nbr_fea, nbr_fea_idx, target) + + atom_fea: torch.Tensor shape (n_i, atom_fea_len). + nbr_fea: torch.Tensor shape (n_i, M, nbr_fea_len). + nbr_fea_idx: torch.LongTensor shape (n_i, M). + target: torch.Tensor shape (1, ). + cif_id: str or int. + + Returns: + N = sum(n_i); N0 = sum(i) + batch_atom_fea: torch.Tensor shape (N, orig_atom_fea_len) + Atom features from atom type. + batch_nbr_fea: torch.Tensor shape (N, M, nbr_fea_len) + Bond features of each atom's M neighbors. + batch_nbr_fea_idx: torch.LongTensor shape (N, M) + Indices of M neighbors of each atom. + crystal_atom_idx: list of torch.LongTensor of length N0 + Mapping from the crystal idx to atom idx. + target: torch.Tensor shape (N, 1) + Target value for prediction. + batch_cif_ids: list. + """ + batch_atom_fea, batch_nbr_fea, batch_nbr_fea_idx = [], [], [] + crystal_atom_idx, batch_target = [], [] + batch_cif_ids = [] + base_idx = 0 + for i, ((atom_fea, nbr_fea, nbr_fea_idx), target, cif_id) in enumerate( + dataset_list + ): + n_i = atom_fea.shape[0] # number of atoms for this crystal + batch_atom_fea.append(atom_fea) + batch_nbr_fea.append(nbr_fea) + batch_nbr_fea_idx.append(nbr_fea_idx + base_idx) + new_idx = torch.LongTensor(np.arange(n_i) + base_idx) + crystal_atom_idx.append(new_idx) + batch_target.append(target) + batch_cif_ids.append(cif_id) + base_idx += n_i + return ( + ( + torch.cat(batch_atom_fea, dim=0), + torch.cat(batch_nbr_fea, dim=0), + torch.cat(batch_nbr_fea_idx, dim=0), + crystal_atom_idx, + ), + torch.stack(batch_target, dim=0), + batch_cif_ids, + ) + + +class GaussianDistance: + """Expands the distance by Gaussian basis. + + Unit: angstrom + """ + + def __init__(self, dmin: float, dmax: float, step: float, var: float = None): + """ + Args: + dmin: float + Minimum interatomic distance. + dmax: float + Maximum interatomic distance. + step: float + Step size for the Gaussian filter. + """ + assert dmin < dmax + assert dmax - dmin > step + self.filter = np.arange(dmin, dmax + step, step) + if var is None: + var = step + self.var = var + + def expand(self, distances: np.ndarray) -> np.ndarray: + """Apply Gaussian disntance filter to a numpy distance array. + + Args: + distance: np.array shape n-d array + A distance matrix of any shape. + + Returns: + expanded_distance: shape (n+1)-d array + Expanded distance matrix with the last dimension of length + len(self.filter). + """ + return np.exp( + -((distances[..., np.newaxis] - self.filter) ** 2) / self.var**2 + ) + + +class AtomInitializer: + """Base class for intializing the vector representation for atoms. + + !!! Use one AtomInitializer per dataset !!! + """ + + def __init__(self, atom_types): + self.atom_types = set(atom_types) + self._embedding = {} + + def get_atom_fea(self, atom_type): + assert atom_type in self.atom_types + return self._embedding[atom_type] + + def load_state_dict(self, state_dict): + self._embedding = state_dict + self.atom_types = set(self._embedding.keys()) + self._decodedict = { + idx: atom_type for atom_type, idx in self._embedding.items() + } + + def state_dict(self): + return self._embedding + + def decode(self, idx): + if not hasattr(self, "_decodedict"): + self._decodedict = { + idx: atom_type for atom_type, idx in self._embedding.items() + } + return self._decodedict[idx] + + +class AtomCustomJSONInitializer(AtomInitializer): + """ + Initialize atom feature vectors using a JSON file, which is a python + dictionary mapping from element number to a list representing the + feature vector of the element. + + """ + + def __init__(self, elem_embedding_file: str): + """ + Args: + elem_embedding_file: str + The path to the .json file. + """ + with open(elem_embedding_file) as f: + elem_embedding = json.load(f) + elem_embedding = {int(key): value for key, value in elem_embedding.items()} + atom_types = set(elem_embedding.keys()) + super(AtomCustomJSONInitializer, self).__init__(atom_types) + for key, value in elem_embedding.items(): + self._embedding[key] = np.array(value, dtype=float) + + +class CIFData(Dataset): + """ + The CIFData dataset is a wrapper for a dataset where the crystal structures + are stored in the form of CIF files. The dataset should have the following + directory structure: + + root_dir + ├── id_prop.csv + ├── atom_init.json + ├── id0.cif + ├── id1.cif + ├── ... + + id_prop.csv: a CSV file with two columns. The first column recodes a + unique ID for each crystal, and the second column recodes the value of + target property. + + atom_init.json: a JSON file that stores the initialization vector for each + element. + + ID.cif: a CIF file that recodes the crystal structure, where ID is the + unique ID for the crystal. + """ + + def __init__( + self, + root_dir: str, + max_num_nbr: int = 12, + radius: int = 8, + dmin: int = 0, + step: float = 0.2, + random_seed: int = 123, + ): + """ + Args: + root_dir: str + The path to the root directory of the dataset. + max_num_nbr: int + The maximum number of neighbors while constructing the crystal graph. + radius: float + The cutoff radius for searching neighbors. + dmin: float + The minimum distance for constructing GaussianDistance. + step: float + The step size for constructing GaussianDistance. + random_seed: int + Random seed for shuffling the dataset. + """ + self.root_dir = root_dir + self.max_num_nbr, self.radius = max_num_nbr, radius + assert os.path.exists(root_dir), "root_dir does not exist!" + id_prop_file = os.path.join(self.root_dir, "id_prop.csv") + assert os.path.exists(id_prop_file), "id_prop.csv does not exist!" + with open(id_prop_file) as f: + reader = csv.reader(f) + self.id_prop_data = [row for row in reader] + random.seed(random_seed) + random.shuffle(self.id_prop_data) + atom_init_file = os.path.join(self.root_dir, "atom_init.json") + assert os.path.exists(atom_init_file), "atom_init.json does not exist!" + self.ari = AtomCustomJSONInitializer(atom_init_file) + self.gdf = GaussianDistance(dmin=dmin, dmax=self.radius, step=step) + + def __len__(self): + return len(self.id_prop_data) + + @functools.lru_cache(maxsize=None) # Cache loaded structures + def __getitem__(self, idx: int) -> Tuple[Any, Any, Any]: # type: ignore + """ + Args: + idx: index. + Returns: + atom_fea: torch.Tensor shape (n_i, atom_fea_len). + nbr_fea: torch.Tensor shape (n_i, M, nbr_fea_len). + nbr_fea_idx: torch.LongTensor shape (n_i, M). + target: torch.Tensor shape (1, ). + cif_id: str or int. + """ + cif_id, target = self.id_prop_data[idx] + crystal = Structure.from_file(os.path.join(self.root_dir, cif_id + ".cif")) + atom_fea = np.vstack( + [ + self.ari.get_atom_fea(crystal[i].specie.number) + for i in range(len(crystal)) + ] + ) + atom_fea = torch.Tensor(atom_fea) # type: ignore + all_nbrs = crystal.get_all_neighbors(self.radius, include_index=True) + all_nbrs = [sorted(nbrs, key=lambda x: x[1]) for nbrs in all_nbrs] + nbr_fea_idx, nbr_fea = [], [] + for nbr in all_nbrs: + if len(nbr) < self.max_num_nbr: + logger.warning( + "{} not find enough neighbors to build graph. " + "If it happens frequently, consider increase " + "radius.".format(cif_id) + ) + nbr_fea_idx.append( + list(map(lambda x: x[2], nbr)) + [0] * (self.max_num_nbr - len(nbr)) + ) + nbr_fea.append( + list(map(lambda x: x[1], nbr)) + + [self.radius + 1.0] * (self.max_num_nbr - len(nbr)) + ) + else: + nbr_fea_idx.append(list(map(lambda x: x[2], nbr[: self.max_num_nbr]))) + nbr_fea.append(list(map(lambda x: x[1], nbr[: self.max_num_nbr]))) + nbr_fea_idx, nbr_fea = np.array(nbr_fea_idx), np.array(nbr_fea) # type: ignore + nbr_fea = self.gdf.expand(nbr_fea) # type: ignore + atom_fea = torch.Tensor(atom_fea) # type: ignore + nbr_fea = torch.Tensor(nbr_fea) # type: ignore + nbr_fea_idx = torch.LongTensor(nbr_fea_idx) # type: ignore + target = torch.Tensor([float(target)]) # type: ignore + return (atom_fea, nbr_fea, nbr_fea_idx), target, cif_id diff --git a/src/gt4sd/frameworks/cgcnn/model.py b/src/gt4sd/frameworks/cgcnn/model.py new file mode 100644 index 000000000..0ea5b2084 --- /dev/null +++ b/src/gt4sd/frameworks/cgcnn/model.py @@ -0,0 +1,274 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""Model module.""" + +from __future__ import division, print_function + +from typing import Any, Dict + +import torch +import torch.nn as nn + + +class ConvLayer(nn.Module): + """Convolutional operation on graphs.""" + + def __init__(self, atom_fea_len: int, nbr_fea_len: int): + """Initialize ConvLayer. + + Args: + atom_fea_len: int + Number of atom hidden features. + nbr_fea_len: int + Number of bond features. + """ + super(ConvLayer, self).__init__() + self.atom_fea_len = atom_fea_len + self.nbr_fea_len = nbr_fea_len + self.fc_full = nn.Linear( + 2 * self.atom_fea_len + self.nbr_fea_len, 2 * self.atom_fea_len + ) + self.sigmoid = nn.Sigmoid() + self.softplus1 = nn.Softplus() + self.bn1 = nn.BatchNorm1d(2 * self.atom_fea_len) + self.bn2 = nn.BatchNorm1d(self.atom_fea_len) + self.softplus2 = nn.Softplus() + + def forward( + self, + atom_in_fea: torch.Tensor, + nbr_fea: torch.Tensor, + nbr_fea_idx: torch.LongTensor, + ) -> torch.Tensor: + """Forward pass. + + N: Total number of atoms in the batch. + M: Max number of neighbors. + + Args: + atom_in_fea: Variable(torch.Tensor) shape (N, atom_fea_len) + Atom hidden features before convolution. + nbr_fea: Variable(torch.Tensor) shape (N, M, nbr_fea_len) + Bond features of each atom's M neighbors. + nbr_fea_idx: torch.LongTensor shape (N, M) + Indices of M neighbors of each atom. + + Returns: + atom_out_fea: nn.Variable shape (N, atom_fea_len) + Atom hidden features after convolution. + + """ + # TODO will there be problems with the index zero padding? + N, M = nbr_fea_idx.shape + # convolution + atom_nbr_fea = atom_in_fea[nbr_fea_idx, :] + total_nbr_fea = torch.cat( + [ + atom_in_fea.unsqueeze(1).expand(N, M, self.atom_fea_len), + atom_nbr_fea, + nbr_fea, + ], + dim=2, + ) + total_gated_fea = self.fc_full(total_nbr_fea) + total_gated_fea = self.bn1( + total_gated_fea.view(-1, self.atom_fea_len * 2) + ).view(N, M, self.atom_fea_len * 2) + nbr_filter, nbr_core = total_gated_fea.chunk(2, dim=2) + nbr_filter = self.sigmoid(nbr_filter) + nbr_core = self.softplus1(nbr_core) + nbr_sumed = torch.sum(nbr_filter * nbr_core, dim=1) + nbr_sumed = self.bn2(nbr_sumed) + out = self.softplus2(atom_in_fea + nbr_sumed) + return out + + +class CrystalGraphConvNet(nn.Module): + """Create a crystal graph convolutional neural network for predicting total material properties.""" + + def __init__( + self, + orig_atom_fea_len: int, + nbr_fea_len: int, + atom_fea_len: int = 64, + n_conv: int = 3, + h_fea_len: int = 128, + n_h: int = 1, + classification: bool = False, + ): + """Initialize CrystalGraphConvNet. + + Args: + orig_atom_fea_len: int + Number of atom features in the input. + nbr_fea_len: int + Number of bond features. + atom_fea_len: int + Number of hidden atom features in the convolutional layers. + n_conv: int + Number of convolutional layers. + h_fea_len: int + Number of hidden features after pooling. + n_h: int + Number of hidden layers after pooling. + """ + super(CrystalGraphConvNet, self).__init__() + self.classification = classification + self.embedding = nn.Linear(orig_atom_fea_len, atom_fea_len) + self.convs = nn.ModuleList( + [ + ConvLayer(atom_fea_len=atom_fea_len, nbr_fea_len=nbr_fea_len) + for _ in range(n_conv) + ] + ) + self.conv_to_fc = nn.Linear(atom_fea_len, h_fea_len) + self.conv_to_fc_softplus = nn.Softplus() + if n_h > 1: + self.fcs = nn.ModuleList( + [nn.Linear(h_fea_len, h_fea_len) for _ in range(n_h - 1)] + ) + self.softpluses = nn.ModuleList([nn.Softplus() for _ in range(n_h - 1)]) + if self.classification: + self.fc_out = nn.Linear(h_fea_len, 2) + else: + self.fc_out = nn.Linear(h_fea_len, 1) + if self.classification: + self.logsoftmax = nn.LogSoftmax(dim=1) + self.dropout = nn.Dropout() + + def forward( + self, + atom_fea: torch.Tensor, + nbr_fea: torch.Tensor, + nbr_fea_idx: torch.LongTensor, + crystal_atom_idx: torch.LongTensor, + ) -> torch.Tensor: + """Forward pass. + + N: Total number of atoms in the batch. + M: Max number of neighbors. + N0: Total number of crystals in the batch. + + Args: + atom_fea: Variable(torch.Tensor) shape (N, orig_atom_fea_len) + Atom features from atom type. + nbr_fea: Variable(torch.Tensor) shape (N, M, nbr_fea_len) + Bond features of each atom's M neighbors. + nbr_fea_idx: torch.LongTensor shape (N, M) + Indices of M neighbors of each atom. + crystal_atom_idx: list of torch.LongTensor of length N0 + Mapping from the crystal idx to atom idx. + + Returns: + prediction: nn.Variable shape (N, ) + Atom hidden features after convolution. + """ + atom_fea = self.embedding(atom_fea) + for conv_func in self.convs: + atom_fea = conv_func(atom_fea, nbr_fea, nbr_fea_idx) + crys_fea = self.pooling(atom_fea, crystal_atom_idx) + crys_fea = self.conv_to_fc(self.conv_to_fc_softplus(crys_fea)) + crys_fea = self.conv_to_fc_softplus(crys_fea) + if self.classification: + crys_fea = self.dropout(crys_fea) + if hasattr(self, "fcs") and hasattr(self, "softpluses"): + for fc, softplus in zip(self.fcs, self.softpluses): + crys_fea = softplus(fc(crys_fea)) + out = self.fc_out(crys_fea) + if self.classification: + out = self.logsoftmax(out) + return out + + def pooling( + self, atom_fea: torch.Tensor, crystal_atom_idx: torch.LongTensor + ) -> torch.Tensor: + """Pooling the atom features to crystal features. + + N: Total number of atoms in the batch. + N0: Total number of crystals in the batch. + + Args: + atom_fea: Variable(torch.Tensor) shape (N, atom_fea_len) + Atom feature vectors of the batch. + crystal_atom_idx: list of torch.LongTensor of length N0 + Mapping from the crystal idx to atom idx. + """ + assert ( + sum([len(idx_map) for idx_map in crystal_atom_idx]) + == atom_fea.data.shape[0] + ) + summed_fea = [ + torch.mean(atom_fea[idx_map], dim=0, keepdim=True) + for idx_map in crystal_atom_idx + ] + return torch.cat(summed_fea, dim=0) + + +class Normalizer: + """Normalize a Tensor and restore it later.""" + + def __init__(self, tensor: torch.Tensor): + """tensor is taken as a sample to calculate the mean and std.""" + self.mean = torch.mean(tensor) + self.std = torch.std(tensor) + + def norm(self, tensor: torch.Tensor) -> torch.Tensor: + """Noramlize a tensor. + + Args: + tensor: tensor to be normalized. + + Returns: + normalized tensor. + """ + return (tensor - self.mean) / self.std + + def denorm(self, normed_tensor: torch.Tensor) -> torch.Tensor: + """Denormalized tensor. + + Args: + tensor: tensor to be denormalized: + + Returns: + denormalized tensor. + """ + return normed_tensor * self.std + self.mean + + def state_dict(self) -> Dict[str, torch.Tensor]: + """Return the state dict of normalizer. + + Returns: + dictionary including the used mean and std values. + """ + return {"mean": self.mean, "std": self.std} + + def load_state_dict(self, state_dict: Dict[str, Any]) -> None: + """Return the state dict of normalizer. + + Args: + mean: mean value to be used for the normalization. + std: std value to be used for the normalization. + """ + self.mean = state_dict["mean"] + self.std = state_dict["std"] diff --git a/src/gt4sd/properties/__init__.py b/src/gt4sd/properties/__init__.py index e731345e3..15c2e5f9c 100644 --- a/src/gt4sd/properties/__init__.py +++ b/src/gt4sd/properties/__init__.py @@ -25,6 +25,7 @@ from typing import Any, Dict, List from .core import PropertyPredictor +from .crystals import CRYSTALS_PROPERTY_PREDICTOR_FACTORY from .molecules import MOLECULE_PROPERTY_PREDICTOR_FACTORY from .proteins import PROTEIN_PROPERTY_PREDICTOR_FACTORY from .scorer import ( diff --git a/src/gt4sd/properties/core.py b/src/gt4sd/properties/core.py index 78dc4cc91..821ebede0 100644 --- a/src/gt4sd/properties/core.py +++ b/src/gt4sd/properties/core.py @@ -32,6 +32,7 @@ class DomainSubmodule(str, Enum): molecules: str = "molecules" properties: str = "properties" + crystals: str = "crystals" class PropertyPredictorParameters(BaseModel): diff --git a/src/gt4sd/properties/crystals/__init__.py b/src/gt4sd/properties/crystals/__init__.py new file mode 100644 index 000000000..0f6bdb58f --- /dev/null +++ b/src/gt4sd/properties/crystals/__init__.py @@ -0,0 +1,71 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +from typing import Dict, Tuple, Type, Union + +from ...algorithms.core import PredictorAlgorithm +from ..core import PropertyPredictor, PropertyPredictorParameters +from .core import ( + AbsoluteEnergy, + AbsoluteEnergyParameters, + BandGap, + BandGapParameters, + BulkModuli, + BulkModuliParameters, + FermiEnergy, + FermiEnergyParameters, + FormationEnergy, + FormationEnergyParameters, + MetalSemiconductorClassifier, + MetalSemiconductorClassifierParameters, + PoissonRatio, + PoissonRatioParameters, + ShearModuli, + ShearModuliParameters, +) + +CRYSTALS_PROPERTY_PREDICTOR_FACTORY: Dict[ + str, + Tuple[ + Union[Type[PropertyPredictor], Type[PredictorAlgorithm]], + Type[PropertyPredictorParameters], + ], +] = { + # inherent properties + "formation_energy": (FormationEnergy, FormationEnergyParameters), + "absolute_energy": (AbsoluteEnergy, AbsoluteEnergyParameters), + "band_gap": (BandGap, BandGapParameters), + "fermi_energy": (FermiEnergy, FermiEnergyParameters), + "bulk_moduli": (BulkModuli, BulkModuliParameters), + "shear_moduli": (ShearModuli, ShearModuliParameters), + "poisson_ratio": (PoissonRatio, PoissonRatioParameters), + "metal_semiconductor_classifier": ( + MetalSemiconductorClassifier, + MetalSemiconductorClassifierParameters, + ), +} + + +AVAILABLE_CRYSTALS_PROPERTY_PREDICTOR = sorted( + CRYSTALS_PROPERTY_PREDICTOR_FACTORY.keys() +) diff --git a/src/gt4sd/properties/crystals/core.py b/src/gt4sd/properties/crystals/core.py new file mode 100644 index 000000000..f27ea8d05 --- /dev/null +++ b/src/gt4sd/properties/crystals/core.py @@ -0,0 +1,274 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +import argparse +import os +from typing import Dict, List + +import torch +from pydantic import Field +from torch.autograd import Variable +from torch.utils.data import DataLoader + +from ...algorithms.core import ( + ConfigurablePropertyAlgorithmConfiguration, + Predictor, + PredictorAlgorithm, +) +from ...frameworks.cgcnn.data import CIFData, collate_pool +from ...frameworks.cgcnn.model import CrystalGraphConvNet, Normalizer +from ..core import DomainSubmodule, S3Parameters + + +class S3ParametersCrystals(S3Parameters): + domain: DomainSubmodule = DomainSubmodule("crystals") + + +class CGCNNParameters(S3ParametersCrystals): + algorithm_name: str = "cgcnn" + batch_size: int = Field(description="Prediction batch size", default=256) + workers: int = Field(description="Number of data loading workers", default=0) + + +class FormationEnergyParameters(CGCNNParameters): + algorithm_application: str = "FormationEnergy" + + +class AbsoluteEnergyParameters(CGCNNParameters): + algorithm_application: str = "AbsoluteEnergy" + + +class BandGapParameters(CGCNNParameters): + algorithm_application: str = "BandGap" + + +class FermiEnergyParameters(CGCNNParameters): + algorithm_application: str = "FermiEnergy" + + +class BulkModuliParameters(CGCNNParameters): + algorithm_application: str = "BulkModuli" + + +class ShearModuliParameters(CGCNNParameters): + algorithm_application: str = "ShearModuli" + + +class PoissonRatioParameters(CGCNNParameters): + algorithm_application: str = "PoissonRatio" + + +class MetalSemiconductorClassifierParameters(CGCNNParameters): + algorithm_application: str = "MetalSemiconductorClassifier" + + +class _CGCNN(PredictorAlgorithm): + """Base class for all cgcnn-based predictive algorithms.""" + + def __init__(self, parameters: CGCNNParameters): + + # Set up the configuration from the parameters + configuration = ConfigurablePropertyAlgorithmConfiguration( + algorithm_type=parameters.algorithm_type, + domain=parameters.domain, + algorithm_name=parameters.algorithm_name, + algorithm_application=parameters.algorithm_application, + algorithm_version=parameters.algorithm_version, + ) + + self.batch_size = parameters.batch_size + self.workers = parameters.workers + + # The parent constructor calls `self.get_model`. + super().__init__(configuration=configuration) + + def get_model(self, resources_path: str) -> Predictor: + """Instantiate the actual model. + + Args: + resources_path: local path to model files. + + Returns: + Predictor: the model. + """ + + existing_models = os.listdir(resources_path) + existing_models = [ + file for file in existing_models if file.endswith(".pth.tar") + ] + + if len(existing_models) > 1: + raise ValueError( + "Only one model should be located in the specified model path." + ) + elif len(existing_models) == 0: + raise ValueError("Model does not exist in the specified model path.") + + model_path = os.path.join(resources_path, existing_models[0]) + + model_checkpoint = torch.load( + model_path, map_location=lambda storage, loc: storage + ) + + model_args = argparse.Namespace(**model_checkpoint["args"]) + + normalizer = Normalizer(torch.zeros(3)) + + checkpoint = torch.load(model_path, map_location=lambda storage, loc: storage) + normalizer.load_state_dict(checkpoint["normalizer"]) + + # Wrapper to get toxicity-endpoint-level predictions + def informative_model(cif_path: str) -> Dict[str, List[float]]: + + dataset = CIFData(cif_path) + test_loader = DataLoader( + dataset, + batch_size=self.batch_size, + num_workers=self.workers, + collate_fn=collate_pool, + ) + + # build model + structures, _, _ = dataset[0] + orig_atom_fea_len = structures[0].shape[-1] + nbr_fea_len = structures[1].shape[-1] # type: ignore + + model = CrystalGraphConvNet( + orig_atom_fea_len, + nbr_fea_len, + atom_fea_len=model_args.atom_fea_len, + n_conv=model_args.n_conv, + h_fea_len=model_args.h_fea_len, + n_h=model_args.n_h, + classification=True if model_args.task == "classification" else False, + ) + + model.load_state_dict(checkpoint["state_dict"]) + + test_preds = [] + test_cif_ids = [] + + for i, (input, target, batch_cif_ids) in enumerate(test_loader): + with torch.no_grad(): + input_var = ( + Variable(input[0]), + Variable(input[1]), + input[2], + input[3], + ) + + # compute output + output = model(*input_var) + + # record loss + if model_args.task == "classification": + test_pred = torch.exp(output.data.cpu()) + test_preds += test_pred[:, 1].tolist() + else: + test_pred = normalizer.denorm(output.data.cpu()) + test_preds += test_pred.view(-1).tolist() + test_cif_ids += batch_cif_ids + + return {"cif_ids": test_cif_ids, "predictions": test_preds} # type: ignore + + return informative_model + + +class FormationEnergy(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the formation energy per atom using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class AbsoluteEnergy(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the absolute energy of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class BandGap(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the band gap of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class FermiEnergy(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the Fermi energy of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class BulkModuli(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the bulk moduli of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class ShearModuli(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the shear moduli of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class PoissonRatio(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts the Poisson ratio of crystals using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text + + +class MetalSemiconductorClassifier(_CGCNN): + @classmethod + def get_description(cls) -> str: + text = """ + This model predicts whether a given crystal is metal or semiconductor using the CGCNN framework. + For more details see: https://doi.org/10.1103/PhysRevLett.120.145301. + """ + return text diff --git a/src/gt4sd/training_pipelines/__init__.py b/src/gt4sd/training_pipelines/__init__.py index fbe1ea16a..ef4143276 100644 --- a/src/gt4sd/training_pipelines/__init__.py +++ b/src/gt4sd/training_pipelines/__init__.py @@ -28,6 +28,13 @@ from ..cli.load_arguments_from_dataclass import extract_fields_from_class from ..tests.utils import exitclose_file_creator +from .cgcnn.core import ( + CGCNNDataArguments, + CGCNNModelArguments, + CGCNNSavingArguments, + CGCNNTrainingArguments, + CGCNNTrainingPipeline, +) from .diffusion.core import ( DiffusionDataArguments, DiffusionForVisionTrainingPipeline, @@ -166,6 +173,11 @@ GFlowNetDataArguments, GFlowNetModelArguments, ), + "cgcnn": ( + CGCNNDataArguments, + CGCNNModelArguments, + CGCNNTrainingArguments, + ), } TRAINING_PIPELINE_MAPPING = { @@ -180,6 +192,7 @@ "regression-transformer-trainer": RegressionTransformerTrainingPipeline, "diffusion-trainer": DiffusionForVisionTrainingPipeline, "gflownet-trainer": GFlowNetTrainingPipeline, + "cgcnn": CGCNNTrainingPipeline, } TRAINING_PIPELINE_ARGUMENTS_FOR_MODEL_SAVING = { @@ -194,6 +207,7 @@ "regression-transformer-trainer": RegressionTransformerSavingArguments, "diffusion-trainer": DiffusionSavingArguments, "gflownet-trainer": GFlowNetSavingArguments, + "cgcnn": CGCNNSavingArguments, } diff --git a/src/gt4sd/training_pipelines/cgcnn/__init__.py b/src/gt4sd/training_pipelines/cgcnn/__init__.py new file mode 100644 index 000000000..74ae1eed4 --- /dev/null +++ b/src/gt4sd/training_pipelines/cgcnn/__init__.py @@ -0,0 +1,24 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""CGCNN training pipeline initialization.""" diff --git a/src/gt4sd/training_pipelines/cgcnn/core.py b/src/gt4sd/training_pipelines/cgcnn/core.py new file mode 100644 index 000000000..f2f67211d --- /dev/null +++ b/src/gt4sd/training_pipelines/cgcnn/core.py @@ -0,0 +1,695 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""Cgcnn training utilities.""" + +import logging +import os +import shutil +import time +from dataclasses import dataclass, field +from random import sample +from typing import Any, Dict, Optional, Tuple, Union + +import numpy as np +import torch +import torch.nn as nn +import torch.optim as optim +from sklearn import metrics +from torch import Tensor +from torch.autograd import Variable +from torch.optim.lr_scheduler import MultiStepLR +from torch.utils.data import DataLoader + +from ...frameworks.cgcnn.data import CIFData, collate_pool, get_train_val_test_loader +from ...frameworks.cgcnn.model import CrystalGraphConvNet, Normalizer +from ..core import TrainingPipeline, TrainingPipelineArguments + +logger = logging.getLogger(__name__) +logger.addHandler(logging.NullHandler()) + + +class CGCNNTrainingPipeline(TrainingPipeline): + """CGCNN training pipelines for crystals.""" + + def train( # type: ignore + self, + training_args: Dict[str, Any], + model_args: Dict[str, Any], + dataset_args: Dict[str, Any], + ) -> None: + """Generic training function for CGCNN models. + + Args: + training_args: training arguments passed to the configuration. + model_args: model arguments passed to the configuration. + dataset_args: dataset arguments passed to the configuration. + + Raises: + NotImplementedError: the generic trainer does not implement the pipeline. + """ + + training_args["disable_cuda"] = ( + training_args["disable_cuda"] or not torch.cuda.is_available() + ) + + if training_args["task"] == "regression": + best_mae_error = 1e10 + else: + best_mae_error = 0.0 + + # load data + dataset = CIFData(dataset_args["datapath"]) + collate_fn = collate_pool + train_loader, val_loader, test_loader = get_train_val_test_loader( # type: ignore + dataset=dataset, + collate_fn=collate_fn, + batch_size=training_args["batch_size"], + num_workers=training_args["workers"], + pin_memory=training_args["disable_cuda"], + train_size=dataset_args["train_size"], + val_size=dataset_args["val_size"], + test_size=dataset_args["test_size"], + return_test=True, + ) + + # obtain target value normalizer + if training_args["task"] == "classification": + normalizer = Normalizer(torch.zeros(2)) + normalizer.load_state_dict({"mean": 0.0, "std": 1.0}) + else: + if len(dataset) < 500: + logger.warning( + "Dataset has less than 500 data points. " + "Lower accuracy is expected. " + ) + sample_data_list = [dataset[i] for i in range(len(dataset))] + else: + sample_data_list = [ + dataset[i] for i in sample(range(len(dataset)), 500) + ] + _, sample_target, _ = collate_pool(sample_data_list) + normalizer = Normalizer(sample_target) + + # build model + structures, _, _ = dataset[0] + orig_atom_fea_len = structures[0].shape[-1] + nbr_fea_len = structures[1].shape[-1] # type: ignore + model = CrystalGraphConvNet( + orig_atom_fea_len, + nbr_fea_len, + atom_fea_len=model_args["atom_fea_len"], + n_conv=model_args["n_conv"], + h_fea_len=model_args["h_fea_len"], + n_h=model_args["n_h"], + classification=True if training_args["task"] == "classification" else False, + ) + if not training_args["disable_cuda"]: + model.cuda() + + # define loss func and optimizer + if training_args["task"] == "classification": + criterion = nn.NLLLoss() + else: + criterion = nn.MSELoss() # type: ignore + + if training_args["optim"] == "SGD": + optimizer = optim.SGD( + model.parameters(), + training_args["lr"], + momentum=training_args["momentum"], + weight_decay=training_args["weight_decay"], + ) + elif training_args["optim"] == "Adam": + optimizer = optim.Adam( # type: ignore + model.parameters(), + training_args["lr"], + weight_decay=training_args["weight_decay"], + ) + else: + raise NameError("Only SGD or Adam is allowed as optimizer") + + # optionally resume from a checkpoint + if training_args["resume"]: + if os.path.isfile(training_args["resume"]): + logger.info("loading checkpoint '{}'".format(training_args["resume"])) + checkpoint = torch.load(training_args["resume"]) + training_args["start_epoch"] = checkpoint["epoch"] + best_mae_error = checkpoint["best_mae_error"] + model.load_state_dict(checkpoint["state_dict"]) + optimizer.load_state_dict(checkpoint["optimizer"]) + normalizer.load_state_dict(checkpoint["normalizer"]) + logger.info( + "loaded checkpoint '{}' (epoch {})".format( + training_args["resume"], checkpoint["epoch"] + ) + ) + else: + logger.info( + "no checkpoint found at '{}'".format(training_args["resume"]) + ) + + scheduler = MultiStepLR( + optimizer, milestones=[training_args["lr_milestone"]], gamma=0.1 + ) + + for epoch in range(training_args["start_epoch"], training_args["epochs"]): + # train for one epoch + train( + train_loader, + model, + criterion, + optimizer, + epoch, + normalizer, + training_args["disable_cuda"], + training_args["task"], + training_args["print_freq"], + ) + + # evaluate on validation set + mae_error = validate( + val_loader, + model, + criterion, + normalizer, + training_args["disable_cuda"], + training_args["task"], + training_args["print_freq"], + test=True, + ) + + if mae_error != mae_error: + raise ValueError("mae_error is NaN") + + scheduler.step() + + # remember the best mae_eror and save checkpoint + if training_args["task"] == "regression": + is_best = mae_error < best_mae_error + best_mae_error = min(mae_error, best_mae_error) + else: + is_best = mae_error > best_mae_error + best_mae_error = max(mae_error, best_mae_error) + save_checkpoint( + { + "epoch": epoch + 1, + "state_dict": model.state_dict(), + "best_mae_error": best_mae_error, + "optimizer": optimizer.state_dict(), + "normalizer": normalizer.state_dict(), + "training_args": training_args, + "model_args": model_args, + "dataset_args": dataset_args, + }, + is_best, + training_args["output_path"], + ) + + # test best model + logger.info("Evaluate Model on Test Set") + best_checkpoint = torch.load("model_best.pth.tar") + model.load_state_dict(best_checkpoint["state_dict"]) + validate( + test_loader, + model, + criterion, + normalizer, + training_args["disable_cuda"], + training_args["task"], + training_args["print_freq"], + test=True, + ) + + +def train( + train_loader: Union[DataLoader[Any], Any], + model: CrystalGraphConvNet, + criterion: Union[nn.NLLLoss, nn.MSELoss], + optimizer: Union[optim.SGD, optim.Adam], + epoch: int, + normalizer: Normalizer, + disable_cuda: bool, + task: str, + print_freq: int, +) -> None: + """Train step for cgcnn models. + + Args: + train_loader: Dataloader for the training set. + model: CGCNN model. + criterion: loss function. + optimizer: Optimizer to be used. + epoch: Epoch number. + normalizer: Normalize. + disable_cuda: Disable CUDA. + task: Training task. + print_freq: Print frequency. + """ + + batch_time = AverageMeter() + data_time = AverageMeter() + losses = AverageMeter() + mae_errors = AverageMeter() + accuracies = AverageMeter() + precisions = AverageMeter() + recalls = AverageMeter() + fscores = AverageMeter() + auc_scores = AverageMeter() + + # switch to train mode + model.train() + + end = time.time() + for i, (input, target, _) in enumerate(train_loader): + # measure data loading time + data_time.update(time.time() - end) + + if not disable_cuda: + input_var = ( + Variable(input[0].cuda(non_blocking=True)), + Variable(input[1].cuda(non_blocking=True)), + input[2].cuda(non_blocking=True), + [crys_idx.cuda(non_blocking=True) for crys_idx in input[3]], + ) + else: + input_var = (Variable(input[0]), Variable(input[1]), input[2], input[3]) + # normalize target + if task == "regression": + target_normed = normalizer.norm(target) + else: + target_normed = target.view(-1).long() + if not disable_cuda: + target_var = Variable(target_normed.cuda(non_blocking=True)) + else: + target_var = Variable(target_normed) + + # compute output + output = model(*input_var) + loss = criterion(output, target_var) + + # measure accuracy and record loss + if task == "regression": + mae_error = mae(normalizer.denorm(output.data.cpu()), target) + losses.update(loss.data.cpu(), target.size(0)) + mae_errors.update(mae_error, target.size(0)) # type: ignore + else: + accuracy, precision, recall, fscore, auc_score = class_eval( + output.data.cpu(), target + ) + losses.update(loss.data.cpu().item(), target.size(0)) + accuracies.update(accuracy, target.size(0)) + precisions.update(precision, target.size(0)) + recalls.update(recall, target.size(0)) + fscores.update(fscore, target.size(0)) + auc_scores.update(auc_score, target.size(0)) + + # compute gradient and do SGD step + optimizer.zero_grad() + loss.backward() + optimizer.step() + + # measure elapsed time + batch_time.update(time.time() - end) + end = time.time() + + if i % print_freq == 0: + if task == "regression": + logger.info( + "Epoch: [{0}][{1}/{2}]\t" + "Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t" + "Data {data_time.val:.3f} ({data_time.avg:.3f})\t" + "Loss {loss.val:.4f} ({loss.avg:.4f})\t" + "MAE {mae_errors.val:.3f} ({mae_errors.avg:.3f})".format( + epoch, + i, + len(train_loader), + batch_time=batch_time, + data_time=data_time, + loss=losses, + mae_errors=mae_errors, + ) + ) + else: + logger.info( + "Epoch: [{0}][{1}/{2}]\t" + "Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t" + "Data {data_time.val:.3f} ({data_time.avg:.3f})\t" + "Loss {loss.val:.4f} ({loss.avg:.4f})\t" + "Accu {accu.val:.3f} ({accu.avg:.3f})\t" + "Precision {prec.val:.3f} ({prec.avg:.3f})\t" + "Recall {recall.val:.3f} ({recall.avg:.3f})\t" + "F1 {f1.val:.3f} ({f1.avg:.3f})\t" + "AUC {auc.val:.3f} ({auc.avg:.3f})".format( + epoch, + i, + len(train_loader), + batch_time=batch_time, + data_time=data_time, + loss=losses, + accu=accuracies, + prec=precisions, + recall=recalls, + f1=fscores, + auc=auc_scores, + ) + ) + + +def validate( + val_loader: Union[DataLoader[Any], Any], + model: CrystalGraphConvNet, + criterion: Union[nn.MSELoss, nn.NLLLoss], + normalizer: Normalizer, + disable_cuda: bool, + task: str, + print_freq: int, + test: bool = False, +) -> float: + """Validation step for cgcnn models. + + Args: + val_loader: Dataloader for the validation set. + model: CGCNN model. + criterion: loss function. + normalizer: Normalize. + disable_cuda: Disable CUDA. + task: Training task. + print_freq: Print frequency. + test: test or only validate using the given dataset. + + Returns: + average auc or mae depending on the training task. + """ + + batch_time = AverageMeter() + losses = AverageMeter() + mae_errors = AverageMeter() + accuracies = AverageMeter() + precisions = AverageMeter() + recalls = AverageMeter() + fscores = AverageMeter() + auc_scores = AverageMeter() + test_targets = [] + test_preds = [] + test_cif_ids = [] + + # switch to evaluate mode + model.eval() + + end = time.time() + for i, (input, target, batch_cif_ids) in enumerate(val_loader): + if not disable_cuda: + with torch.no_grad(): + input_var = ( + Variable(input[0].cuda(non_blocking=True)), + Variable(input[1].cuda(non_blocking=True)), + input[2].cuda(non_blocking=True), + [crys_idx.cuda(non_blocking=True) for crys_idx in input[3]], + ) + else: + with torch.no_grad(): + input_var = (Variable(input[0]), Variable(input[1]), input[2], input[3]) + if task == "regression": + target_normed = normalizer.norm(target) + else: + target_normed = target.view(-1).long() + if not disable_cuda: + with torch.no_grad(): + target_var = Variable(target_normed.cuda(non_blocking=True)) + else: + with torch.no_grad(): + target_var = Variable(target_normed) + + # compute output + output = model(*input_var) + loss = criterion(output, target_var) + + # measure accuracy and record loss + if task == "regression": + mae_error = mae(normalizer.denorm(output.data.cpu()), target) + losses.update(loss.data.cpu().item(), target.size(0)) + mae_errors.update(mae_error, target.size(0)) # type: ignore + if test: + test_pred = normalizer.denorm(output.data.cpu()) + test_target = target + test_preds += test_pred.view(-1).tolist() + test_targets += test_target.view(-1).tolist() + test_cif_ids += batch_cif_ids + else: + accuracy, precision, recall, fscore, auc_score = class_eval( + output.data.cpu(), target + ) + losses.update(loss.data.cpu().item(), target.size(0)) + accuracies.update(accuracy, target.size(0)) + precisions.update(precision, target.size(0)) + recalls.update(recall, target.size(0)) + fscores.update(fscore, target.size(0)) + auc_scores.update(auc_score, target.size(0)) + if test: + test_pred = torch.exp(output.data.cpu()) + test_target = target + assert test_pred.shape[1] == 2 + test_preds += test_pred[:, 1].tolist() + test_targets += test_target.view(-1).tolist() + test_cif_ids += batch_cif_ids + + # measure elapsed time + batch_time.update(time.time() - end) + end = time.time() + + if i % print_freq == 0: + if task == "regression": + logger.info( + "Test: [{0}/{1}]\t" + "Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t" + "Loss {loss.val:.4f} ({loss.avg:.4f})\t" + "MAE {mae_errors.val:.3f} ({mae_errors.avg:.3f})".format( + i, + len(val_loader), + batch_time=batch_time, + loss=losses, + mae_errors=mae_errors, + ) + ) + else: + logger.info( + "Test: [{0}/{1}]\t" + "Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t" + "Loss {loss.val:.4f} ({loss.avg:.4f})\t" + "Accu {accu.val:.3f} ({accu.avg:.3f})\t" + "Precision {prec.val:.3f} ({prec.avg:.3f})\t" + "Recall {recall.val:.3f} ({recall.avg:.3f})\t" + "F1 {f1.val:.3f} ({f1.avg:.3f})\t" + "AUC {auc.val:.3f} ({auc.avg:.3f})".format( + i, + len(val_loader), + batch_time=batch_time, + loss=losses, + accu=accuracies, + prec=precisions, + recall=recalls, + f1=fscores, + auc=auc_scores, + ) + ) + + if task == "regression": + logger.info("MAE {mae_errors.avg:.3f}".format(mae_errors=mae_errors)) + return mae_errors.avg + else: + logger.info("AUC {auc.avg:.3f}".format(auc=auc_scores)) + return auc_scores.avg + + +def mae(prediction: Tensor, target: Tensor) -> Tensor: + """Computes the mean absolute error between prediction and target. + + Args: + prediction: torch.Tensor (N, 1) + target: torch.Tensor (N, 1) + + Returns: + the computed mean absolute error. + """ + return torch.mean(torch.abs(target - prediction)) + + +def class_eval( + prediction: Tensor, target: Tensor +) -> Tuple[float, float, float, float, float]: + """Class evaluation. + + Args: + prediction: Predictions. + target: Groundtruth. + + Returns: + Computed accuracy, precision, recall, fscore, and auc_score. + + """ + + prediction = np.exp(prediction.numpy()) + target = target.numpy() + pred_label = np.argmax(prediction, axis=1) + target_label = np.squeeze(target) + if not target_label.shape: + target_label = np.asarray([target_label]) + if prediction.shape[1] == 2: + precision, recall, fscore, _ = metrics.precision_recall_fscore_support( + target_label, pred_label, average="binary" + ) + auc_score = metrics.roc_auc_score(target_label, prediction[:, 1]) + accuracy = metrics.accuracy_score(target_label, pred_label) + else: + raise NotImplementedError + return accuracy, precision, recall, fscore, auc_score + + +class AverageMeter: + """Computes and stores the average and current value.""" + + def __init__(self): + """Initialize an AverageMeter object.""" + + self.reset() + + def reset(self) -> None: + """Reset values to 0.""" + + self.val = 0.0 + self.avg = 0.0 + self.sum = 0.0 + self.count = 0 + + def update(self, val: float, n: int = 1) -> None: + """Update values of the AverageMeter. + + Args: + val: value to be added. + n: count. + """ + self.val = val + self.sum += val * n + self.count += n + self.avg = self.sum / self.count + + +def save_checkpoint( + state: object, is_best: bool, path: str = ".", filename: str = "checkpoint.pth.tar" +) -> None: + """Save CGCNN checkpoint. + + Args: + state: checkpoint's object. + is_best: whether the given checkpoint has the best performance or not. + path: path to save the checkpoint. + filename: checkpoint's filename. + + """ + + torch.save(state, os.path.join(path, filename)) + if is_best: + shutil.copyfile(filename, os.path.join(path, "model_best.pth.tar")) + + +@dataclass +class CGCNNDataArguments(TrainingPipelineArguments): + """Data arguments related to diffusion trainer.""" + + __name__ = "dataset_args" + + datapath: str = field( + metadata={ + "help": "Path to the dataset." + "The dataset should follow the directory structure as described in https://github.com/txie-93/cgcnn" + }, + ) + train_size: Optional[int] = field( + default=None, metadata={"help": "Number of training data to be loaded."} + ) + val_size: Optional[int] = field( + default=None, metadata={"help": "Number of validation data to be loaded."} + ) + test_size: Optional[int] = field( + default=None, metadata={"help": "Number of testing data to be loaded."} + ) + + +@dataclass +class CGCNNModelArguments(TrainingPipelineArguments): + """Model arguments related to CGCNN trainer.""" + + __name__ = "model_args" + + atom_fea_len: int = field( + default=64, metadata={"help": "Number of hidden atom features in conv layers."} + ) + h_fea_len: int = field( + default=128, metadata={"help": "Number of hidden features after pooling."} + ) + n_conv: int = field(default=3, metadata={"help": "Number of conv layers."}) + n_h: int = field( + default=1, metadata={"help": "Number of hidden layers after pooling."} + ) + + +@dataclass +class CGCNNTrainingArguments(TrainingPipelineArguments): + """Training arguments related to CGCNN trainer.""" + + __name__ = "training_args" + + task: str = field( + default="regression", + metadata={"help": "Select the type of the task."}, + ) + output_path: str = field( + default=".", + metadata={"help": "Path to the store the checkpoints."}, + ) + disable_cuda: bool = field(default=False, metadata={"help": "Disable CUDA."}) + workers: int = field( + default=0, metadata={"help": "Number of data loading workers."} + ) + epochs: int = field(default=30, metadata={"help": "Number of total epochs to run."}) + start_epoch: int = field( + default=0, metadata={"help": "Manual epoch number (useful on restarts)."} + ) + batch_size: int = field(default=256, metadata={"help": "Mini-batch size."}) + lr: float = field(default=0.01, metadata={"help": "Initial learning rate."}) + lr_milestone: float = field( + default=100, metadata={"help": "Milestone for scheduler."} + ) + momentum: float = field(default=0.9, metadata={"help": "Momentum."}) + weight_decay: float = field(default=0.0, metadata={"help": "Weight decay."}) + print_freq: int = field(default=10, metadata={"help": "Print frequency."}) + resume: str = field(default="", metadata={"help": "Path to latest checkpoint."}) + optim: str = field(default="SGD", metadata={"help": "Optimizer."}) + + +@dataclass +class CGCNNSavingArguments(TrainingPipelineArguments): + """Saving arguments related to CGCNN trainer.""" + + __name__ = "saving_args" diff --git a/src/gt4sd/training_pipelines/tests/test_training_cgnn.py b/src/gt4sd/training_pipelines/tests/test_training_cgnn.py new file mode 100644 index 000000000..cc194ed0d --- /dev/null +++ b/src/gt4sd/training_pipelines/tests/test_training_cgnn.py @@ -0,0 +1,73 @@ +# +# MIT License +# +# Copyright (c) 2022 GT4SD team +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +"""CGCNN trainer unit tests.""" + +import shutil +import tempfile +from typing import Any, Dict, cast + +import pytest + +from gt4sd.training_pipelines import TRAINING_PIPELINE_MAPPING, CGCNNTrainingPipeline + +template_config = { + "model_args": { + "atom_fea_len": 64, + "h_fea_len": 128, + "n_conv": 3, + "n_h": 1, + }, + "training_args": { + "task": "classification", + "disable_cuda": True, + "epochs": 5, + "batch_size": 256, + "lr": 0.01, + "momentum": 0.9, + "weight_decay": 0.0, + "optim": "SGD", + }, + "dataset_args": { + "datapath": "./data/cgcnn_sample_classification", + }, +} + + +@pytest.mark.skip(reason="we need to add support for dataset buckets") +def test_train(): + + pipeline = TRAINING_PIPELINE_MAPPING.get("cgcnn") + + assert pipeline is not None + + TEMPORARY_DIRECTORY = tempfile.mkdtemp() + + test_pipeline = cast(CGCNNTrainingPipeline, pipeline()) + + config: Dict[str, Any] = template_config.copy() + config["training_args"]["output_path"] = TEMPORARY_DIRECTORY + + test_pipeline.train(**config) + + shutil.rmtree(TEMPORARY_DIRECTORY)