10 Commits

Author SHA1 Message Date
  i-robot c08f83e1a2
!2435 refactor_orb_v2 1 week ago
  i-robot c2a52f5fe0
!2440 refactor diffcsp 1 week ago
  i-robot 13306ed300
!2437 refactor nequip: move mindchemistry to models 1 week ago
  i-robot 9aa42ecd21
!2438 refactor matformer 1 week ago
  i-robot f47d2919a8
!2445 【MindSPONGE】move AF3 from legacy-master to master 1 week ago
  Yuheng Wang 8c46d291d5 move AF3 from legacy2master 1 week ago
  liumintao2025 623d21c0cb refactor nequip 1 week ago
  liumintao2025 f0ba5a4913 refactor matformer 1 week ago
  liumintao2025 28a0f7d674 refactor orb 1 week ago
  liumintao2025 791d01248e refactor diffcsp 1 week ago
100 changed files with 605 additions and 6821 deletions
Split View
  1. +11
    -0
      .jenkins/check/config/filter_cppcheck.txt
  2. +21
    -0
      .jenkins/check/config/filter_cpplint.txt
  3. +3
    -1
      .jenkins/check/config/filter_linklint.txt
  4. +137
    -0
      .jenkins/check/config/filter_pylint.txt
  5. +34
    -0
      .jenkins/check/config/whitelizard.txt
  6. +9
    -4
      MindChem/applications/diffcsp/README.md
  7. +5
    -1
      MindChem/applications/diffcsp/README_EN.md
  8. +0
    -0
      MindChem/applications/diffcsp/data/__init__.py
  9. +2
    -1
      MindChem/applications/diffcsp/data/crysloader.py
  10. +0
    -0
      MindChem/applications/diffcsp/data/dataloader.py
  11. +0
    -59
      MindChem/applications/diffcsp/mindchemistry/__init__.py
  12. +0
    -0
      MindChem/applications/diffcsp/models/__init__.py
  13. +5
    -7
      MindChem/applications/diffcsp/models/cspnet.py
  14. +3
    -3
      MindChem/applications/diffcsp/models/diffusion.py
  15. +0
    -0
      MindChem/applications/diffcsp/models/graph.py
  16. +0
    -0
      MindChem/applications/diffcsp/models/loss.py
  17. +2
    -2
      MindChem/applications/diffcsp/train.py
  18. +10
    -6
      MindChem/applications/matformer/README.md
  19. +10
    -6
      MindChem/applications/matformer/README_CN.md
  20. +1
    -1
      MindChem/applications/matformer/config.yaml
  21. +22
    -0
      MindChem/applications/matformer/data/__init__.py
  22. +139
    -173
      MindChem/applications/matformer/matformer_application.ipynb
  23. +5
    -5
      MindChem/applications/matformer/matformer_application_EN.ipynb
  24. +0
    -64
      MindChem/applications/matformer/mindchemistry/__init__.py
  25. +0
    -25
      MindChem/applications/matformer/mindchemistry/cell/__init__.py
  26. +0
    -38
      MindChem/applications/matformer/mindchemistry/cell/activation.py
  27. +0
    -600
      MindChem/applications/matformer/mindchemistry/cell/basic_block.py
  28. +0
    -19
      MindChem/applications/matformer/mindchemistry/cell/matformer/__init__.py
  29. +0
    -101
      MindChem/applications/matformer/mindchemistry/cell/matformer/matformer.py
  30. +0
    -158
      MindChem/applications/matformer/mindchemistry/cell/matformer/transformer.py
  31. +0
    -165
      MindChem/applications/matformer/mindchemistry/cell/message_passing.py
  32. +0
    -141
      MindChem/applications/matformer/mindchemistry/cell/nequip.py
  33. +0
    -15
      MindChem/applications/matformer/mindchemistry/graph/__init__.py
  34. +0
    -19
      MindChem/applications/matformer/mindchemistry/so2_conv/__init__.py
  35. +0
    -64
      MindChem/applications/matformer/mindchemistry/so2_conv/init_edge_rot_mat.py
  36. BIN
      MindChem/applications/matformer/mindchemistry/so2_conv/jd.pkl
  37. +0
    -244
      MindChem/applications/matformer/mindchemistry/so2_conv/so2.py
  38. +0
    -156
      MindChem/applications/matformer/mindchemistry/so2_conv/so3.py
  39. +0
    -61
      MindChem/applications/matformer/mindchemistry/so2_conv/wigner.py
  40. +0
    -18
      MindChem/applications/matformer/mindchemistry/utils/__init__.py
  41. +0
    -128
      MindChem/applications/matformer/mindchemistry/utils/check_func.py
  42. +0
    -85
      MindChem/applications/matformer/mindchemistry/utils/load_config.py
  43. +20
    -0
      MindChem/applications/matformer/models/__init__.py
  44. +0
    -0
      MindChem/applications/matformer/models/graph/__init__.py
  45. +0
    -0
      MindChem/applications/matformer/models/graph/dataloader.py
  46. +0
    -0
      MindChem/applications/matformer/models/graph/graph.py
  47. +0
    -0
      MindChem/applications/matformer/models/graph/loss.py
  48. +0
    -0
      MindChem/applications/matformer/models/graph/normlization.py
  49. +3
    -3
      MindChem/applications/matformer/models/matformer.py
  50. +2
    -2
      MindChem/applications/matformer/models/transformer.py
  51. +0
    -0
      MindChem/applications/matformer/models/utils.py
  52. +4
    -4
      MindChem/applications/matformer/predict.py
  53. +4
    -4
      MindChem/applications/matformer/train.py
  54. +0
    -64
      MindChem/applications/nequip/mindchemistry/__init__.py
  55. +0
    -24
      MindChem/applications/nequip/mindchemistry/cell/__init__.py
  56. +0
    -38
      MindChem/applications/nequip/mindchemistry/cell/activation.py
  57. +0
    -600
      MindChem/applications/nequip/mindchemistry/cell/basic_block.py
  58. +0
    -120
      MindChem/applications/nequip/mindchemistry/cell/convolution.py
  59. +0
    -146
      MindChem/applications/nequip/mindchemistry/cell/embedding.py
  60. +0
    -19
      MindChem/applications/nequip/mindchemistry/cell/matformer/__init__.py
  61. +0
    -15
      MindChem/applications/nequip/mindchemistry/graph/__init__.py
  62. +0
    -408
      MindChem/applications/nequip/mindchemistry/graph/dataloader.py
  63. +0
    -278
      MindChem/applications/nequip/mindchemistry/graph/normlization.py
  64. +0
    -19
      MindChem/applications/nequip/mindchemistry/so2_conv/__init__.py
  65. +0
    -64
      MindChem/applications/nequip/mindchemistry/so2_conv/init_edge_rot_mat.py
  66. BIN
      MindChem/applications/nequip/mindchemistry/so2_conv/jd.pkl
  67. +0
    -260
      MindChem/applications/nequip/mindchemistry/so2_conv/so2.py
  68. +0
    -156
      MindChem/applications/nequip/mindchemistry/so2_conv/so3.py
  69. +0
    -61
      MindChem/applications/nequip/mindchemistry/so2_conv/wigner.py
  70. +0
    -18
      MindChem/applications/nequip/mindchemistry/utils/__init__.py
  71. +0
    -128
      MindChem/applications/nequip/mindchemistry/utils/check_func.py
  72. +6
    -0
      MindChem/applications/nequip/models/__init__.py
  73. +1
    -1
      MindChem/applications/nequip/models/convolution.py
  74. +0
    -0
      MindChem/applications/nequip/models/embedding.py
  75. +0
    -0
      MindChem/applications/nequip/models/graph.py
  76. +1
    -1
      MindChem/applications/nequip/models/load_config.py
  77. +0
    -0
      MindChem/applications/nequip/models/message_passing.py
  78. +1
    -1
      MindChem/applications/nequip/models/nequip.py
  79. +0
    -0
      MindChem/applications/nequip/models/utils.py
  80. +59
    -81
      MindChem/applications/nequip/nequip.ipynb
  81. +2
    -2
      MindChem/applications/nequip/nequip_en.ipynb
  82. +1
    -1
      MindChem/applications/nequip/predict.py
  83. +1
    -1
      MindChem/applications/nequip/rmd.yaml
  84. +1
    -1
      MindChem/applications/nequip/src/predicter.py
  85. +1
    -1
      MindChem/applications/nequip/src/trainer.py
  86. +1
    -1
      MindChem/applications/nequip/train.py
  87. +38
    -26
      MindChem/applications/orb/README.md
  88. +38
    -26
      MindChem/applications/orb/README_CN.md
  89. +1
    -1
      MindChem/applications/orb/configs/config_eval.yaml
  90. +1
    -1
      MindChem/applications/orb/configs/config_parallel.yaml
  91. +0
    -66
      MindChem/applications/orb/mindchemistry/__init__.py
  92. +0
    -22
      MindChem/applications/orb/mindchemistry/cell/__init__.py
  93. +0
    -38
      MindChem/applications/orb/mindchemistry/cell/activation.py
  94. +0
    -600
      MindChem/applications/orb/mindchemistry/cell/basic_block.py
  95. +0
    -120
      MindChem/applications/orb/mindchemistry/cell/convolution.py
  96. +0
    -146
      MindChem/applications/orb/mindchemistry/cell/embedding.py
  97. +0
    -166
      MindChem/applications/orb/mindchemistry/cell/message_passing.py
  98. +0
    -15
      MindChem/applications/orb/mindchemistry/graph/__init__.py
  99. +0
    -408
      MindChem/applications/orb/mindchemistry/graph/dataloader.py
  100. +0
    -294
      MindChem/applications/orb/mindchemistry/graph/graph.py

+ 11
- 0
.jenkins/check/config/filter_cppcheck.txt View File

@@ -2,3 +2,14 @@
"mindscience/MindElec/mindelec/ccsrc/api/python/pybind_register.cc" "syntaxError"
"mindscience/MindElec/mindelec/ccsrc/scientific_compute/pointcloud/material_analyse.cc" "useStlAlgorithm"
"mindscience/MindElec/mindelec/ccsrc/scientific_compute/pointcloud/tensor_initializer.cc" "useStlAlgorithm"
#MindSPONGE
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc" "shadowFunction"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc" "useStlAlgorithm"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/msa_conversion_pybind.cc" "variableScope"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_altlocs.cc" "shadowVariable"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_altlocs.cc" "useStlAlgorithm"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc" "unsignedLessThanZero"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/string_array_pybind.cc" "shadowVariable"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/string_array_pybind.cc" "useStlAlgorithm"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/string_array_pybind.cc" "pointerSize"


+ 21
- 0
.jenkins/check/config/filter_cpplint.txt View File

@@ -597,3 +597,24 @@
"mindscience/MindSPONGE/mindsponge/ccsrc/molecular_dynamics/barostats/MC_barostat.cu" "whitespace/parens"
"mindscience/MindSPONGE/mindsponge/ccsrc/molecular_dynamics/thermostats/Andersen_thermostat.cu" "whitespace/parens"
"mindscience/MindSPONGE/mindsponge/ccsrc/molecular_dynamics/common.cuh" "build/include_subdir"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc" "whitespace/parens"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc" "runtime/references"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mkdssp_pybind.cc" "build/include_order"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mkdssp_pybind.cc" "whitespace/ending_newline"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/msa_conversion_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_utils_pybind.cc" "whitespace/braces"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_utils_pybind.cc" "whitespace/parens"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_utils_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/cpp/msa_profile_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/cpp/msa_profile_pybind.cc" "whitespace/ending_newline"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_atom_site_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_layout_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/aggregation_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc" "whitespace/braces"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/fasta_iterator_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/membership_pybind.cc" "build/include"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_altlocs.cc" "runtime/references"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_altlocs.cc" "build/c++17"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mkdssp_pybind.cc" "build/c++17"

+ 3
- 1
.jenkins/check/config/filter_linklint.txt View File

@@ -4,4 +4,6 @@
https://api.colabfold.com
https://a3m.mmseqs.com
https://www.mindspore.cn/community/SIG/detail/?name=mindflow+SIG
https://www.mindspore.cn/sig/MindSpore%20Science
https://www.mindspore.cn/sig/MindSpore%20Science
https://mmcif.wwpdb.org/dictionaries/mmcif_pdbx_v50.dic/Items/_entity.type.html
https://gitee.com/mindspore/mindscience/tree/main/MindSPONGE/applications/AlphaFold3

+ 137
- 0
.jenkins/check/config/filter_pylint.txt View File

@@ -398,6 +398,143 @@
"mindscience/MindSPONGE/applications/rf_diffusion/run_inference.py" "no-value-for-parameter"
"mindscience/MindSPONGE/applications/rf_diffusion/rfdiffusion/inference/ab_util.py" "missing-function-docstring"
"mindscience/MindSPONGE/applications/rf_diffusion/run_inference.py" "invalid-name"

# DeepMind AlphaFold3
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/constants/chemical_components.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/nhmmer.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/pipeline/structure_cleaning.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/mmcif.py" "multiple-statements"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/base_config.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/__init__.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/resources.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/table.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/constants/chemical_components.py" "assigning-non-slot"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/post_processing.py" "unused-variable"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/hmmalign.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/jackhmmer.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/post_processing.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/template_store.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/scoring/covalent_bond_cleaning.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/scoring/scoring.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/mmcif.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/msa_features.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/msa_identifiers.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/base_config.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/bonds.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/msa_store.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/testing/data.py" "pointless-statement"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/parsers.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/mmcif.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/template_realign.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/resources.py" "function-redefined"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/constants/chemical_components.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/pipeline.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/protein_data_processing.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/merging_features.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/confidence_types.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/scoring/scoring.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/folding_input.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/templates.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/pipeline/pipeline.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/chemical_components.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/resources.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/msa.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/hmmsearch.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/featurisation.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/post_processing.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/resources.py" "pointless-statement"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/structure_stores.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/msa_config.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/subprocess_utils.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/mmcif.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/parsing.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/testing/data.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/scoring/alignment.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/mmcif.py" "invalid-name"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/test_utils.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/pipeline/inter_chain_bonds.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/testing/data.py" "function-redefined"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/hmmsearch.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/hmmsearch.py" "useless-object-inheritance"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/post_processing.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/constants/periodic_table.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure_tables.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/hmmbuild.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/constants/mmcif_names.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/testing/data.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/tools/msa_tool.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/attention/attention_base.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/struct_of_array.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/struct_of_array.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/feat_batch.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/diffusion_head.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "invalid-name"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "pointless-statement"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/atom_layout/atom_layout.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/base_modules.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/featurization.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/base_modules.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/base_modules.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/params.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/atom_cross_attention.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/attention/ms_attention.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/attention/ms_attention.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/attention/attention.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/utils.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/atom_cross_attention.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/common/precision.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/__init__.py" "invalid-name"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/vector.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold_data_test.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/struct_of_array.py" "cell-var-from-loop"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/vector.py" "invalid-name"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/diffusion_head.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "unexpected-keyword-arg"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold_test_v2.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/featurization.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "function-redefined"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "len-as-condition"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "unsupported-membership-test"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/featurization.py" "redefined-argument-from-local"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/distogram_head.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "unsupported-assignment-operation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/struct_of_array.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/__init__.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "multiple-statements"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "no-else-return"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "too-many-function-args"
"mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/mapping.py" "unused-variable"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/features.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/model.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/template_modules.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/confidence_head.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/diffusion_transformer.py" "syntax-error"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/components/utils.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mmcif_metadata.py" "bad-continuation"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/template_modules.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/load_ckpt.py" "unused-import"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/load_ckpt.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/geometry/rigid_matrix_vector.py" "bad-whitespace"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/load_ckpt.py" "no-value-for-parameter"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/model.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/modules.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/gated_linear_unit/gated_linear_unit_base.py" "pointless-statement"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/modules.py" "missing-docstring"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/load_ckpt.py" "protected-access"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/model.py" "redefined-argument-from-local"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/triangle.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/utils/gated_linear_unit/gated_linear_unit_base.py" "unused-argument"
"mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mmcif_metadata.py" "unused-argument"
"mindscience/MindSPONGE/applications/proteinmpnn/helper_scripts/assign_fixed_chains.py" "invalid-name"
"mindscience/MindSPONGE/applications/proteinmpnn/helper_scripts/make_bias_AA.py" "invalid-name"
"mindscience/MindSPONGE/applications/proteinmpnn/helper_scripts/make_fixed_positions_dict.py" "invalid-name"


+ 34
- 0
.jenkins/check/config/whitelizard.txt View File

@@ -26,6 +26,40 @@ mindscience/MindSPONGE/applications/rf_diffusion/env/se3_transformer/model/layer
mindscience/MindSPONGE/applications/rf_diffusion/env/se3_transformer/model/layers/convolution.py:construct
mindscience/MindSPONGE/applications/rf_diffusion/run_inference.py:main
mindscience/MindSPONGE/applications/rf_diffusion/rfdiffusion/inference/ab_util.py:ab_write_pdblines
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/folding_input.py:from_alphafoldserver_fold_job
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/folding_input.py:from_json
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc:alphafold3::GetEscapeQuote
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc:alphafold3::CifDict::ToString
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_pybind.cc:alphafold3::Gather
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_pybind.cc:alphafold3::CifDictGetArray
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_pybind.cc:alphafold3::RegisterModuleCifDict
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_utils_pybind.cc:alphafold3::FixArginine::Fix
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_layout_lib.cc:alphafold3::MmcifLayout::Create
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc:alphafold3::GetBondAtomIndices
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/pipeline.py:__init__
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/pipeline/pipeline.py:process_item
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/common/folding_input.py:from_json
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/pipeline/structure_cleaning.py:clean_structure
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/mmcif_metadata.py:add_metadata_to_mmcif
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_lib.cc:alphafold3::CifDict::ToString
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/parsers/cpp/cif_dict_pybind.cc:alphafold3::RegisterModuleCifDict
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_layout_lib.cc:alphafold3::MmcifLayout::Create
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc:alphafold3::GetBondAtomIndices
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/parsing.py:from_res_arrays, from_atom_arrays, _maybe_add_missing_scheme_tables, from_sequences_and_bonds
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure.py:filter
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure.py:merge_chains
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure.py:order_and_drop_atoms_to_match
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure_tables.py:to_mmcif_sequence_and_entity_tables
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/structure/structure_tables.py:tables_from_atom_arrays
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/model.py:get_inference_result
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/features.py:compute_features
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/features.py:get_reference
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/features.py:tokenizer
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/atom_layout/atom_layout.py:residues_from_structure
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/atom_layout/atom_layout.py:make_flat_atom_layout
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/model/diffusion/atom_cross_attention.py:construct
mindscience/MindSPONGE/applications/AlphaFold3/alphafold3/data/parsers.py:convert_stockholm_to_a3m
mindscience/MindSPONGE/applications/AlphaFold3/run_alphafold_test_v2.py:test_inference
mindscience/MindSPONGE/applications/proteinmpnn/proteinmpnn/util_protein_mpnn.py:ab_write_pdblines
mindscience/MindSPONGE/applications/proteinmpnn/proteinmpnn/util_protein_mpnn.py:parse_PDB_biounits
mindscience/MindSPONGE/applications/proteinmpnn/proteinmpnn/util_protein_mpnn.py:parse_PDB


+ 9
- 4
MindChem/applications/diffcsp/README.md View File

@@ -9,7 +9,7 @@

## 环境要求

> 1. 安装`mindspore(2.3.0)`
> 1. 安装`mindspore(2.7.0)`
> 2. 安装依赖包:`pip install -r requirement.txt`

## 快速入门
@@ -37,12 +37,16 @@ diffcsp
data_utils.py 数据集处理工具
dataset.py 读取数据集
crysloader.py 数据集载入器
dataloader.py 构建数据集
└─models
cspnet.py 基于图神经网络的去噪器模块
diffusion.py 扩散模型模块
diff_utils.py 工具模块
infer_utils.py 推理工具模块
train_utils.py 训练工具模块
graph.py 工具
loss.py 损失模块


```

@@ -70,9 +74,10 @@ diffcsp

更改config文件,设置训练参数:
> 1. 设置训练的dataset,见dataset字段
> 2. 设置去噪器模型的配置,见model字段
> 3. 设置训练保存的权重文件,更改train.ckpt_dir文件夹名称和checkpoint.last_path权重文件名称
> 4. 其它训练设置见train字段
> 2. 设置训练的轮次,见epoch_size字段
> 3. 设置去噪器模型的配置,见model字段
> 4. 设置训练保存的权重文件,更改train.ckpt_dir文件夹名称和checkpoint.last_path权重文件名称
> 5. 其它训练设置见train字段

```bash
pip install -r requirement.txt


+ 5
- 1
MindChem/applications/diffcsp/README_EN.md View File

@@ -8,7 +8,7 @@ DiffCSP is a diffusion-model-based deep learning framework for crystal structure

## Environment Requirements

1. Install `mindspore (2.3.0)`
1. Install `mindspore (2.7.0)`
2. Install dependencies: `pip install -r requirement.txt`

## Quick Start
@@ -37,12 +37,15 @@ diffcsp
data_utils.py Dataset processing utilities
dataset.py Dataset reader
crysloader.py Dataset loader
dataloader.py Dataset construction
└─models
cspnet.py GNN-based denoiser module
diffusion.py Diffusion model module
diff_utils.py Utilities
infer_utils.py Inference utilities
train_utils.py Training utilities
graph.py Utilities
loss.py Loss
```

## Dataset Download
@@ -70,6 +73,7 @@ Download the `Mindchemistry/mindchemistry` package to the current directory.
Edit the config file to set training parameters:

- Set the training dataset (see the `dataset` field).
- To set the training rounds (see the epoch_size field).
- Configure the denoiser model (see the `model` field).
- Set the directory and filename for saving checkpoints by editing `train.ckpt_dir` and `checkpoint.last_path`.
- Other training settings are under the `train` field.


+ 0
- 0
MindChem/applications/diffcsp/data/__init__.py View File


+ 2
- 1
MindChem/applications/diffcsp/data/crysloader.py View File

@@ -17,7 +17,7 @@ import numpy as np
from mindspore import Tensor, ops
import mindspore as ms

from mindchemistry.graph.dataloader import DataLoaderBase, CommonData
from data.dataloader import DataLoaderBase, CommonData


class Crysloader(DataLoaderBase):
@@ -42,6 +42,7 @@ class Crysloader(DataLoaderBase):
shuffle_dataset=True,
max_node=None,
max_edge=None):
super().__init__(batch_size, node_attr, edge_attr, edge_index)
self.batch_size = batch_size
self.edge_index = edge_index
self.index = 0


MindChem/applications/diffcsp/mindchemistry/graph/dataloader.py → MindChem/applications/diffcsp/data/dataloader.py View File


+ 0
- 59
MindChem/applications/diffcsp/mindchemistry/__init__.py View File

@@ -1,59 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Initialization for MindChemistry APIs."""

import time
import mindspore as ms
from mindspore import log as logger
from mindscience.e3nn import *
from .graph import *

__all__ = []

def _mindspore_version_check():
"""
Check MindSpore version for MindChemistry.

Raises:
ImportError: If MindSpore cannot be imported.
"""
try:
_ = ms.__version__
except ImportError as exc:
raise ImportError(
"Cannot find MindSpore in the current environment. Please install "
"MindSpore before using MindChemistry, by following the instruction at "
"https://www.mindspore.cn/install"
) from exc

ms_version = ms.__version__[:5]
required_mindspore_version = "1.8.1"

if ms_version < required_mindspore_version:
logger.warning(
f"Current version of MindSpore ({ms_version}) is not compatible with MindChemistry. "
f"Some functions might not work or even raise errors. Please install MindSpore "
f"version >= {required_mindspore_version}. For more details about dependency settings, "
f"please check the instructions at the MindSpore official website "
f"https://www.mindspore.cn/install or check the README.md at "
f"https://gitee.com/mindspore/mindscience"
)

for i in range(3, 0, -1):
logger.warning(f"Please pay attention to the above warning, countdown: {i}")
time.sleep(1)


_mindspore_version_check()

+ 0
- 0
MindChem/applications/diffcsp/models/__init__.py View File


+ 5
- 7
MindChem/applications/diffcsp/models/cspnet.py View File

@@ -17,10 +17,8 @@ import math

import numpy as np
import mindspore
import mindspore.nn as nn
import mindspore.ops as ops
from mindspore import Tensor
from mindchemistry.graph.graph import (AggregateEdgeToNode,
from mindspore import Tensor,nn,ops
from models.graph import (AggregateEdgeToNode,
AggregateNodeToGlobal, LiftGlobalToNode)

MAX_ATOMIC_NUM = 100
@@ -38,7 +36,7 @@ class SinusoidsEmbedding(nn.Cell):
"""

def __init__(self, n_frequencies=10, n_space=3):
super(SinusoidsEmbedding, self).__init__()
super().__init__()
self.n_frequencies = n_frequencies
self.n_space = n_space
self.frequencies = 2 * math.pi * np.arange(self.n_frequencies)
@@ -102,7 +100,7 @@ class CSPLayer(nn.Cell):
act_fn (nn): The activation function used in the layer. Defaults to nn.SiLU().
dis_emb (object): The embbing method used for edge features. Defaults to None.
"""
super(CSPLayer, self).__init__()
super().__init__()
self.dis_dim = 3
self.dis_emb = dis_emb
if dis_emb is not None:
@@ -209,7 +207,7 @@ class CSPNet(nn.Cell):
num_freqs (int): The number of frequencies for Fourier embedding for
edge features. Defaults to 128.
"""
super(CSPNet, self).__init__()
super().__init__()
self.node_embedding = nn.Embedding(max_atoms, hidden_dim)
self.atom_latent_emb = nn.Dense(hidden_dim + latent_dim, hidden_dim)
self.act_fn = nn.SiLU()


+ 3
- 3
MindChem/applications/diffcsp/models/diffusion.py View File

@@ -18,7 +18,7 @@ import math
import mindspore as ms
import mindspore.numpy as mnp
from mindspore import nn, ops
from mindchemistry.graph.graph import (AggregateNodeToGlobal, LiftGlobalToNode)
from models.graph import (AggregateNodeToGlobal, LiftGlobalToNode)

from models.diff_utils import (BetaScheduler, SigmaScheduler,
d_log_p_wrapped_normal_ms)
@@ -38,7 +38,7 @@ class SinusoidalTimeEmbeddings(nn.Cell):
Referring the implementation details in the paper Attention is all you need. """

def __init__(self, dim):
super(SinusoidalTimeEmbeddings, self).__init__()
super().__init__()
self.dim = dim

def construct(self, time):
@@ -118,7 +118,7 @@ class CSPDiffusion(nn.Cell):
sigma_end (float): The ending sigma used in fractiaonal coordinates SDEs.
Defaults to 0.5.
"""
super(CSPDiffusion, self).__init__()
super().__init__()
self.beta_scheduler = BetaScheduler(timesteps=timesteps,
scheduler_mode=scheduler_mode)
self.sigma_scheduler = SigmaScheduler(timesteps=timesteps,


MindChem/applications/diffcsp/mindchemistry/graph/graph.py → MindChem/applications/diffcsp/models/graph.py View File


MindChem/applications/matformer/mindchemistry/graph/loss.py → MindChem/applications/diffcsp/models/loss.py View File


+ 2
- 2
MindChem/applications/diffcsp/train.py View File

@@ -22,7 +22,7 @@ import numpy as np
import mindspore as ms
from mindspore import nn, set_seed
from mindspore.amp import all_finite
from mindchemistry.graph.loss import L2LossMask
from models.loss import L2LossMask
from models.cspnet import CSPNet
from models.diffusion import CSPDiffusion
from models.train_utils import LossRecord
@@ -46,7 +46,7 @@ def main():
args = parse_args()
ms.set_context(device_target=args.device_target, device_id=args.device_id)

with open(args.config, 'r') as stream:
with open(args.config, 'r', encoding="utf-8") as stream:
config = yaml.safe_load(stream)

ckpt_dir = config['train']["ckpt_dir"]


+ 10
- 6
MindChem/applications/matformer/README.md View File

@@ -152,16 +152,20 @@ The figure below shows the formation energy predictions from a fully trained Mat
Log output from `train.py`:

```log
INFO:root:Loading from saved file...
INFO:root:The model you built has 2786689 parameters.
INFO:root:Starting new training process
INFO:root:Start to initialise train loader
INFO:root:Start to initialise eval loader
INFO:root:load from existing check point................
INFO:root:finish load from existing checkpoint, start training from epoch: 1
INFO:root:change learning rate to current step: 953
INFO:root:current learning rate: 9.345746e-08
INFO:root:Start to initialise train_loader
INFO:root:Start to initialise eval_loader
INFO:root:+++++++++++++++ start traning +++++++++++++++++++++
INFO:root:==============================step: 0 ,epoch: 0
INFO:root:learning rate: 4e-05
INFO:root:train mse loss: 0.8999285
INFO:root:learning rate: 9.345746e-08
INFO:root:train mse loss: 0.09808009
INFO:root:is_finite: True
INFO:root:training time: 51.66963744163513
INFO:root:traning time: 22.13266158103943
.
.
.


+ 10
- 6
MindChem/applications/matformer/README_CN.md View File

@@ -152,16 +152,20 @@ python predict.py
`train.py`运行日志如下:

```log
INFO:root:Loading from saved file...
INFO:root:The model you built has 2786689 parameters.
INFO:root:Starting new training process
INFO:root:Start to initialise train loader
INFO:root:Start to initialise eval loader
INFO:root:load from existing check point................
INFO:root:finish load from existing checkpoint, start training from epoch: 1
INFO:root:change learning rate to current step: 953
INFO:root:current learning rate: 9.345746e-08
INFO:root:Start to initialise train_loader
INFO:root:Start to initialise eval_loader
INFO:root:+++++++++++++++ start traning +++++++++++++++++++++
INFO:root:==============================step: 0 ,epoch: 0
INFO:root:learning rate: 4e-05
INFO:root:train mse loss: 0.8999285
INFO:root:learning rate: 9.345746e-08
INFO:root:train mse loss: 0.09808009
INFO:root:is_finite: True
INFO:root:training time: 51.66963744163513
INFO:root:traning time: 22.13266158103943
.
.
.


+ 1
- 1
MindChem/applications/matformer/config.yaml View File

@@ -1,6 +1,6 @@
train:
device: Ascend
device_id: 0
device_id: 1
dataset_dir: "./dataset"
ckpt_dir: "./ckpt"
props: "formation_energy_peratom"


+ 22
- 0
MindChem/applications/matformer/data/__init__.py View File

@@ -0,0 +1,22 @@
"""
Matformer Data Module
---------------------

This subpackage provides data loading, preprocessing, and feature construction
for the **Matformer** model. It is independent of MindScience's core `data` module
to allow customized graph-based molecular representations.

Main Components:
- data.py: Core dataset management and property mapping.
- features.py: Feature engineering for atomic and bond attributes.
- generate.py: Functions to build and prepare training datasets.
- graphs.py: Molecular graph construction and neighborhood computation.

Typical Usage:
from data.generate import get_prop_model

dataset = get_prop_model(
dataset_path="datasets/mptrj_ase.db",
task="property_prediction"
)
"""

+ 139
- 173
MindChem/applications/matformer/matformer_application.ipynb
File diff suppressed because it is too large
View File


+ 5
- 5
MindChem/applications/matformer/matformer_application_EN.ipynb View File

@@ -99,7 +99,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "a7fc7b46",
"metadata": {},
"outputs": [
@@ -129,10 +129,10 @@
"from mindspore import set_seed\n",
"from mindspore import nn\n",
"from mindspore.amp import all_finite\n",
"from mindchemistry.cell.matformer.matformer import Matformer\n",
"from mindchemistry.cell.matformer.utils import LossRecord, OneCycleLr\n",
"from mindchemistry.graph.loss import L1LossMask, L2LossMask\n",
"from mindchemistry.graph.dataloader import DataLoaderBase as DataLoader\n",
"from models.matformer import Matformer\n",
"from models.utils import LossRecord, OneCycleLr\n",
"from models.graph.loss import L1LossMask, L2LossMask\n",
"from models.graph.dataloader import DataLoaderBase as DataLoader\n",
"\n",
"config_path = \"config.yaml\"\n",
"with open(config_path, 'r', encoding='utf-8') as stream:\n",


+ 0
- 64
MindChem/applications/matformer/mindchemistry/__init__.py View File

@@ -1,64 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Initialization for MindChemistry APIs."""

import time
import mindspore as ms
from mindspore import log as logger
from mindscience.e3nn import *
from .cell import *
from .utils import *
from .graph import *
from .so2_conv import *

__all__ = []
__all__.extend(cell.__all__)
__all__.extend(utils.__all__)

def _mindspore_version_check():
"""
Check MindSpore version for MindChemistry.

Raises:
ImportError: If MindSpore cannot be imported.
"""
try:
_ = ms.__version__
except ImportError as exc:
raise ImportError(
"Cannot find MindSpore in the current environment. Please install "
"MindSpore before using MindChemistry, by following the instruction at "
"https://www.mindspore.cn/install"
) from exc

ms_version = ms.__version__[:5]
required_mindspore_version = "1.8.1"

if ms_version < required_mindspore_version:
logger.warning(
f"Current version of MindSpore ({ms_version}) is not compatible with MindChemistry. "
f"Some functions might not work or even raise errors. Please install MindSpore "
f"version >= {required_mindspore_version}. For more details about dependency settings, "
f"please check the instructions at the MindSpore official website "
f"https://www.mindspore.cn/install or check the README.md at "
f"https://gitee.com/mindspore/mindscience"
)

for i in range(3, 0, -1):
logger.warning(f"Please pay attention to the above warning, countdown: {i}")
time.sleep(1)


_mindspore_version_check()

+ 0
- 25
MindChem/applications/matformer/mindchemistry/cell/__init__.py View File

@@ -1,25 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""initialization for cells"""

from .nequip import Nequip
from .basic_block import AutoEncoder, FCNet, MLPNet
from .matformer import *

__all__ = [
"Nequip", 'AutoEncoder'
]

__all__.extend(matformer.__all__)

+ 0
- 38
MindChem/applications/matformer/mindchemistry/cell/activation.py View File

@@ -1,38 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""get activation function."""
from __future__ import absolute_import

from mindspore import ops
from mindspore.nn.layer import activation

_activation = {
'softmax': activation.Softmax,
'logsoftmax': activation.LogSoftmax,
'relu': activation.ReLU,
'silu': activation.SiLU,
'relu6': activation.ReLU6,
'tanh': activation.Tanh,
'gelu': activation.GELU,
'fast_gelu': activation.FastGelu,
'elu': activation.ELU,
'sigmoid': activation.Sigmoid,
'prelu': activation.PReLU,
'leakyrelu': activation.LeakyReLU,
'hswish': activation.HSwish,
'hsigmoid': activation.HSigmoid,
'logsigmoid': activation.LogSigmoid,
'sin': ops.Sin
}

+ 0
- 600
MindChem/applications/matformer/mindchemistry/cell/basic_block.py View File

@@ -1,600 +0,0 @@
# Copyright 2023 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""basic"""
from __future__ import absolute_import

from collections.abc import Sequence
from typing import Union

from mindspore import nn
from mindspore.nn.layer import activation
from mindspore import ops, float16, float32, Tensor
from mindspore.common.initializer import Initializer

from .activation import _activation


def _get_dropout(dropout_rate):
"""
Gets the dropout functions.

Inputs:
dropout_rate (Union[int, float]): The dropout rate of the dropout function.
If dropout_rate was int or not in range (0,1], it would be rectify to closest float value.

Returns:
Function, the dropout function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_dropout
>>> dropout = get_dropout(0.5)
>>> dropout.set_train
Dropout<keep_prob=0.5>
"""
dropout_rate = float(max(min(dropout_rate, 1.), 1e-7))
return nn.Dropout(keep_prob=dropout_rate)


def _get_layernorm(channel, epsilon):
"""
Gets the layer normalization functions.

Inputs:
channel (Union[int, list]): The normalized shape of the layer normalization function.
If channel was int, it would be wrap into a list.
epsilon (float): The epsilon of the layer normalization function.

Returns:
Function, the layer normalization function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layernorm
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> layernorm = get_layernorm([2], 1e-7)
>>> output = layernorm(input_x)
>>> print(output)
[[ 9.99999881e-01, -9.99999881e-01],
[-1.00000000e+00, 1.00000000e+00]]
"""
if isinstance(channel, int):
channel = [channel]
return nn.LayerNorm(channel, epsilon=epsilon)


def _get_activation(name):
"""
Gets the activation function.

Inputs:
name (Union[str, None]): The name of the activation function. If name was None, it would return [].

Returns:
Function, the activation function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_activation
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> sigmoid = _get_activation('sigmoid')
>>> output = sigmoid(input_x)
>>> print(output)
[[0.7685248 0.5249792 ]
[0.54983395 0.96083426]]
"""
if name is None:
return []
if isinstance(name, str):
name = name.lower()
if name not in _activation:
return activation.get_activation(name)
return _activation.get(name)()
return name


def _get_layer_arg(arguments, index):
"""
Gets the argument of each network layers.

Inputs:
arguments (Union[str, int, float, List, None]): The arguments of each layers.
If arguments was List return the argument at the index of the List.
index (int): The index of layer in the network

Returns:
Argument of the indexed layer.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = _get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = _get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
if isinstance(arguments, list):
if len(arguments) <= index:
if len(arguments) == 1:
return [] if arguments[0] is None else arguments[0]
return []
return [] if arguments[index] is None else arguments[index]
return [] if arguments is None else arguments


def get_linear_block(
in_channels,
out_channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
"""
Gets the linear block list.

Inputs:
in_channels (int): The number of input channel.
out_channels (int): The number of output channel.
weight_init (Union[str, float, mindspore.common.initializer]): The initializer of the weights of dense layer
has_bias (bool): The switch for whether dense layer has bias.
bias_init (Union[str, float, mindspore.common.initializer]): The initializer of the bias of dense layer
has_dropout (bool): The switch for whether linear block has a dropout layer.
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
has_layernorm (bool): The switch for whether linear block has a layer normalization layer.
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
has_activation (bool): The switch for whether linear block has an activation layer.
act (Union[str, None]): The activation function in linear block

Returns:
List of mindspore.nn.Cell, linear block list .

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
dense = nn.Dense(
in_channels, out_channels, weight_init=weight_init, bias_init=bias_init, has_bias=has_bias, activation=None
)
dropout = _get_dropout(dropout_rate) if (has_dropout is True) else []
layernorm = _get_layernorm(out_channels, layernorm_epsilon) if (has_layernorm is True) else []
act = _get_activation(act) if (has_activation is True) else []
block_list = [dense, dropout, layernorm, act]
while [] in block_list:
block_list.remove([])
return block_list


class FCNet(nn.Cell):
r"""
The Fully Connected Network. Applies a series of fully connected layers to the incoming data.

Args:
channels (List): the list of numbers of channel of each fully connected layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = FCNet([3, 16, 32, 16, 8])
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.has_bias = has_bias
self.bias_init = bias_init
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.layernorm_epsilon = layernorm_epsilon
self.has_activation = has_activation
self.activation = act
self.network = nn.SequentialCell(self._create_network())

def _create_network(self):
""" create the network """
cell_list = []
for i in range(len(self.channels) - 1):
cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return cell_list

def construct(self, x):
return self.network(x)


class MLPNet(nn.Cell):
r"""
The MLPNet Network. Applies a series of fully connected layers to the incoming data among which hidden layers have
same number of channels.

Args:
in_channels (int): the number of input layer channel.
out_channels (int): the number of output layer channel.
layers (int): the number of layers.
neurons (int): the number of channels of hidden layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1] .
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = MLPNet(in_channels=3, out_channels=8, layers=5, neurons=32)
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
in_channels,
out_channels,
layers,
neurons,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = (in_channels,) + (layers - 2) * \
(neurons,) + (out_channels,)
self.network = FCNet(
channels=self.channels,
weight_init=weight_init,
has_bias=has_bias,
bias_init=bias_init,
has_dropout=has_dropout,
dropout_rate=dropout_rate,
has_layernorm=has_layernorm,
layernorm_epsilon=layernorm_epsilon,
has_activation=has_activation,
act=act
)

def construct(self, x):
return self.network(x)


class MLPMixPrecision(nn.Cell):
"""MLPMixPrecision
"""

def __init__(
self,
input_dim: int,
hidden_dims: Sequence,
short_cut=False,
batch_norm=False,
activation_fn='relu',
has_bias=False,
weight_init: Union[Initializer, str] = 'xavier_uniform',
bias_init: Union[Initializer, str] = 'zeros',
dropout=0,
dtype=float32
):
super().__init__()
self.dtype = dtype
self.div = ops.Div()

self.dims = [input_dim] + hidden_dims
self.short_cut = short_cut
self.nonlinear_const = 1.0
if isinstance(activation_fn, str):
self.activation = _activation.get(activation_fn)()
if activation_fn is not None and activation_fn == 'silu':
self.nonlinear_const = 1.679177
else:
self.activation = activation_fn
self.dropout = None
if dropout:
self.dropout = nn.Dropout(dropout)
fcs = [
nn.Dense(dim, self.dims[i + 1], weight_init=weight_init, bias_init=bias_init,
has_bias=has_bias).to_float(self.dtype) for i, dim in enumerate(self.dims[:-1])
]
self.layers = nn.CellList(fcs)
self.batch_norms = None
if batch_norm:
bns = [nn.BatchNorm1d(dim) for dim in self.dims[1:-1]]
self.batch_norms = nn.CellList(bns)

def construct(self, inputs):
"""construct

Args:
inputs: inputs

Returns:
inputs
"""
hidden = inputs
norm_from_last = 1.0
for i, layer in enumerate(self.layers):
sqrt_dim = ops.sqrt(Tensor(float(self.dims[i])))
layer_hidden = layer(hidden)
if self.dtype == float16:
layer_hidden = layer_hidden.astype(float16)
hidden = self.div(layer_hidden * norm_from_last, sqrt_dim)
norm_from_last = self.nonlinear_const
if i < len(self.layers) - 1:
if self.batch_norms is not None:
x = hidden.flatten(0, -2)
hidden = self.batch_norms[i](x).view_as(hidden)
if self.activation is not None:
hidden = self.activation(hidden)
if self.dropout is not None:
hidden = self.dropout(hidden)
if self.short_cut and hidden.shape == hidden.shape:
hidden += inputs
return hidden


class AutoEncoder(nn.Cell):
r"""
The AutoEncoder Network.
Applies an encoder to get the latent code and applies a decoder to get the reconstruct data.

Args:
channels (list): The number of channels of each encoder and decoder layer.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .
out_act (Union[None, str, mindspore.nn.Cell]): The activation function to output layer. Default: ``None`` .

Inputs:
- **x** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **latents** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.
- **x_recon** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry import AutoEncoder
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = AutoEncoder([3, 6, 2])
>>> output = net(inputs)
>>> print(output[0].shape, output[1].shape)
(2, 2) (2, 3)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu',
out_act=None
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.bias_init = bias_init
self.has_bias = has_bias
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.has_activation = has_activation
self.layernorm_epsilon = layernorm_epsilon
self.activation = act
self.output_activation = out_act
self.encoder = nn.SequentialCell(self._create_encoder())
self.decoder = nn.SequentialCell(self._create_decoder())

def _create_encoder(self):
""" create the network encoder """
encoder_cell_list = []
for i in range(len(self.channels) - 1):
encoder_cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return encoder_cell_list

def _create_decoder(self):
""" create the network decoder """
decoder_channels = self.channels[::-1]
decoder_weight_init = self.weight_init[::-1] if isinstance(self.weight_init, list) else self.weight_init
decoder_bias_init = self.bias_init[::-1] if isinstance(self.bias_init, list) else self.bias_init
decoder_cell_list = []
for i in range(len(decoder_channels) - 1):
decoder_cell_list += get_linear_block(
decoder_channels[i],
decoder_channels[i + 1],
weight_init=_get_layer_arg(decoder_weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(decoder_bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
if self.output_activation is not None:
decoder_cell_list.append(_get_activation(self.output_activation))
return decoder_cell_list

def encode(self, x):
return self.encoder(x)

def decode(self, z):
return self.decoder(z)

def construct(self, x):
latents = self.encode(x)
x_recon = self.decode(latents)
return x_recon, latents

+ 0
- 19
MindChem/applications/matformer/mindchemistry/cell/matformer/__init__.py View File

@@ -1,19 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""initialization for matformer"""

from .matformer import Matformer

__all__ = ['Matformer']

+ 0
- 101
MindChem/applications/matformer/mindchemistry/cell/matformer/matformer.py View File

@@ -1,101 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Matformer"""
from mindspore import nn, ops
import mindspore as ms
from mindchemistry.graph.graph import AggregateNodeToGlobal
from mindchemistry.cell.matformer.utils import RBFExpansion
from mindchemistry.cell.matformer.transformer import MatformerConv, Silu


class Matformer(nn.Cell):
"""Matformer class"""

def __init__(self, config):
"""init"""
super().__init__()

use_fp16 = config['use_fp16']
self.classification = config['classification']
self.use_angle = config['use_angle']

self.global_dtype = ms.float32
if use_fp16:
self.global_dtype = ms.float16

self.atom_embedding = nn.Dense(
config['atom_input_features'], config['node_features']
).to_float(self.global_dtype)

##################
self.rbf_0 = RBFExpansion(vmin=0, vmax=8.0, bins=config['edge_features'],)
self.rbf_1 = nn.Dense(config['edge_features'], config['node_features']).to_float(self.global_dtype)
self.rbf_2 = ops.Softplus()
self.rbf_3 = nn.Dense(config['node_features'], config['node_features']).to_float(self.global_dtype)

self.angle_lattice = config["angle_lattice"]

self.att_layers = nn.CellList(
[
MatformerConv(in_channels=config['node_features'], out_channels=config['node_features'],
heads=config['node_layer_head'], edge_dim=config['node_features'])
for _ in range(config['conv_layers'])
]
)

self.fc = nn.SequentialCell(
nn.Dense(config['node_features'], config['fc_features']).to_float(self.global_dtype),
Silu().to_float(ms.float32)
)

self.fc_out = nn.Dense(
config['fc_features'], config['output_features']
)

self.link = None
self.link_name = config['link']
if config['link'] == "identity":
self.link = lambda x: x

self.dim_size = config["batch_size_max"]
self.aggregator_to_global = AggregateNodeToGlobal("mean")

def construct(self, data_x, data_edge_attr, data_edge_index, data_batch, node_mask, edge_mask, node_num):
"""construct"""
node_features = self.atom_embedding(data_x)
edge_feat = ops.norm(data_edge_attr, dim=1)
edge_features = self.rbf_0(edge_feat)
edge_features = ops.mul(edge_features, ops.reshape(edge_mask, (-1, 1)))
edge_features = self.rbf_1(edge_features)
edge_features = self.rbf_2(edge_features)
edge_features = self.rbf_3(edge_features)

node_features = self.att_layers[0](node_features, data_edge_index, edge_features, node_mask, edge_mask,
node_num)
node_features = self.att_layers[1](node_features, data_edge_index, edge_features, node_mask, edge_mask,
node_num)
node_features = self.att_layers[2](node_features, data_edge_index, edge_features, node_mask, edge_mask,
node_num)
node_features = self.att_layers[3](node_features, data_edge_index, edge_features, node_mask, edge_mask,
node_num)
node_features = self.att_layers[4](node_features, data_edge_index, edge_features, node_mask, edge_mask,
node_num)

features = self.aggregator_to_global(node_features, data_batch, dim_size=self.dim_size, mask=node_mask)

features = self.fc(features)
out = self.fc_out(features)
res = ops.squeeze(out)
return res

+ 0
- 158
MindChem/applications/matformer/mindchemistry/cell/matformer/transformer.py View File

@@ -1,158 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""transformer file"""
import mindspore as ms
from mindspore import ops, Tensor, nn
from mindchemistry.graph.graph import LiftNodeToEdge, AggregateEdgeToNode
from mindchemistry.graph.normlization import BatchNormMask


class Silu(nn.Cell):
"""Silu"""

def __init__(self):
"""init"""
super().__init__()
self.sigmoid = nn.Sigmoid()

def construct(self, x):
"""construct"""
return ops.mul(x, self.sigmoid(x))


class MatformerConv(nn.Cell):
"""MatformerConv"""

def __init__(
self,
in_channels=None,
out_channels=None,
heads=1,
concat=True,
beta=False,
edge_dim=None,
bias=True,
root_weight=True,
use_fp16=False
):
"""init"""
super().__init__()
self.in_channels = in_channels
self.out_channels = out_channels
self.heads = heads
self.beta = beta and root_weight
self.root_weight = root_weight
self.concat = concat
self.edge_dim = edge_dim
self.global_dtype = ms.float32
if use_fp16:
self.global_dtype = ms.float16

if isinstance(in_channels, int):
in_channels = (in_channels, in_channels)

self.lin_key = nn.Dense(in_channels[0], heads * out_channels).to_float(self.global_dtype)
self.lin_query = nn.Dense(in_channels[1], heads * out_channels).to_float(self.global_dtype)
self.lin_value = nn.Dense(in_channels[0], heads * out_channels).to_float(self.global_dtype)

if edge_dim is not None:
self.lin_edge = nn.Dense(edge_dim, heads * out_channels, has_bias=False).to_float(self.global_dtype)

if concat:
self.lin_concate = nn.Dense(heads * out_channels, out_channels).to_float(self.global_dtype)

self.lin_skip = nn.Dense(in_channels[1], out_channels, has_bias=bias).to_float(self.global_dtype)
if self.beta:
self.lin_beta = nn.Dense(3 * out_channels, 1, has_bias=False).to_float(self.global_dtype)

self.lin_msg_update = nn.Dense(out_channels * 3, out_channels * 3).to_float(self.global_dtype)

self.msg_layer = nn.SequentialCell(nn.Dense(out_channels * 3, out_channels).to_float(self.global_dtype),
nn.LayerNorm((out_channels,), epsilon=0.00001).to_float(self.global_dtype))

self.bn = BatchNormMask(out_channels).to_float(ms.float32)
self.sigmoid = nn.Sigmoid()
self.layer_norm = nn.LayerNorm((out_channels * 3,), epsilon=0.00001).to_float(ms.float32)
self.silu = Silu()
self.lift_to_edge_i = LiftNodeToEdge(dim=1)
self.lift_to_edge_j = LiftNodeToEdge(dim=0)
self.aggregate_to_node = AggregateEdgeToNode(mode="add", dim=1)

self.layer_norm_after_lin_msg_update = nn.LayerNorm((out_channels * 3,), epsilon=0.00001).to_float(ms.float32)
self.gelu = nn.GELU()

def construct(self, x, edge_index, edge_attr, node_mask, edge_mask, node_num):
"""construct"""
h_num, c_num = self.heads, self.out_channels
if isinstance(x, Tensor):
x = (x, x)
query = self.lin_query(x[1]).view(-1, h_num, c_num)
key = self.lin_key(x[0]).view(-1, h_num, c_num)
value = self.lin_value(x[0]).view(-1, h_num, c_num)
out = self.propagate(edge_index, edge_mask, query, key, value, edge_attr)
if self.concat:
out = out.view(-1, self.heads * self.out_channels)
out = self.lin_concate(out)
else:
out = ops.mean(out, axis=1)

node_mask = ops.reshape(node_mask, (-1, 1))
out = self.bn(out, node_mask, node_num)
out = self.silu(out)

if self.root_weight:
x_r = self.lin_skip(x[1])
x_r = ops.mul(x_r, node_mask).astype(ms.float32)
if self.beta:
beta = self.lin_beta(ops.cat([out, x_r, out - x_r], axis=-1))
beta = self.sigmoid(beta)
out = beta * x_r + (1 - beta) * out
else:
out += x_r

return out

def message(self, query_i, key_i, key_j, value_j, value_i,
edge_attr) -> Tensor:
"""message"""
if self.lin_edge is not None:
edge_attr = self.lin_edge(edge_attr).view(-1, self.heads, self.out_channels)

query_i = ops.cat((query_i, query_i, query_i), axis=-1)
key_j = ops.cat((key_i, key_j, edge_attr), axis=-1)
alpha = ops.div(ops.mul(query_i.astype(ms.float32), key_j.astype(ms.float32)),
ops.sqrt(ms.Tensor(self.out_channels * 3, ms.float32)))
out = ops.cat((value_i, value_j, edge_attr), axis=-1)

out_norm = self.gelu(self.layer_norm_after_lin_msg_update(self.lin_msg_update(out)))
out = out_norm * self.sigmoid(self.layer_norm(alpha.view(-1, self.heads, 3 * self.out_channels)))
out = self.msg_layer(out)
return out

def propagate(self, edge_index, trans_scatter_mask, query, key, value, edge_attr):
"""propagate"""
query_i = self.lift_to_edge_i(query, edge_index)
key_i = self.lift_to_edge_i(key, edge_index)
value_i = self.lift_to_edge_i(value, edge_index)
key_j = self.lift_to_edge_j(key, edge_index)
value_j = self.lift_to_edge_j(value, edge_index)
out = self.message(query_i, key_i, key_j, value_j, value_i, edge_attr)
out = self.aggregate_to_node(out, edge_index, dim_size=query.shape[0], mask=trans_scatter_mask)
return out

def __repr__(self) -> str:
"""__repr__"""
return (f'{self.__class__.__name__}({self.in_channels}, '
f'{self.out_channels}, heads={self.heads})')

+ 0
- 165
MindChem/applications/matformer/mindchemistry/cell/message_passing.py View File

@@ -1,165 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""MessagePassing"""
from mindspore import nn, ops, float32

from mindscience.e3nn.o3 import Irreps
from mindscience.e3nn.nn import Gate, NormActivation
from .convolution import Convolution, shift_softplus

acts = {
"abs": ops.abs,
"tanh": ops.tanh,
"ssp": shift_softplus,
"silu": ops.silu,
}


class Compose(nn.Cell):
def __init__(self, first, second):
super().__init__()
self.first = first
self.second = second

def construct(self, *inputs):
x = self.first(*inputs)
x = self.second(x)
return x


class MessagePassing(nn.Cell):
"""MessagePassing"""
# pylint: disable=W0102
def __init__(
self,
irreps_node_input,
irreps_node_attr,
irreps_node_hidden,
irreps_node_output,
irreps_edge_attr,
irreps_edge_scalars,
convolution_kwargs={},
num_layers=3,
resnet=False,
nonlin_type="gate",
nonlin_scalars={"e": "ssp", "o": "tanh"},
nonlin_gates={"e": "ssp", "o": "abs"},
dtype=float32,
ncon_dtype=float32
):
super().__init__()
if nonlin_type not in ('gate', 'norm'):
raise ValueError(f"Unexpected nonlin_type {nonlin_type}.")

nonlin_scalars = {
1: nonlin_scalars["e"],
-1: nonlin_scalars["o"],
}
nonlin_gates = {
1: nonlin_gates["e"],
-1: nonlin_gates["o"],
}

self.irreps_node_input = Irreps(irreps_node_input)
self.irreps_node_hidden = Irreps(irreps_node_hidden)
self.irreps_node_output = Irreps(irreps_node_output)
self.irreps_node_attr = Irreps(irreps_node_attr)
self.irreps_edge_attr = Irreps(irreps_edge_attr)
self.irreps_edge_scalars = Irreps(irreps_edge_scalars)

irreps_node = self.irreps_node_input
irreps_prev = irreps_node
self.layers = nn.CellList()
self.resnets = []

for _ in range(num_layers):
tmp_irreps = irreps_node * self.irreps_edge_attr

irreps_scalars = Irreps(
[
(mul, ir)
for mul, ir in self.irreps_node_hidden
if ir.l == 0 and ir in tmp_irreps
]
).simplify()
irreps_gated = Irreps(
[
(mul, ir)
for mul, ir in self.irreps_node_hidden
if ir.l > 0 and ir in tmp_irreps
]
)

if nonlin_type == "gate":
ir = "0e" if Irreps("0e") in tmp_irreps else "0o"
irreps_gates = Irreps([(mul, ir)
for mul, _ in irreps_gated]).simplify()

nonlinear = Gate(
irreps_scalars,
[acts[nonlin_scalars[ir.p]] for _, ir in irreps_scalars],
irreps_gates,
[acts[nonlin_gates[ir.p]] for _, ir in irreps_gates],
irreps_gated,
dtype=dtype,
ncon_dtype=ncon_dtype
)

conv_irreps_out = nonlinear.irreps_in
else:
conv_irreps_out = (irreps_scalars + irreps_gated).simplify()

nonlinear = NormActivation(
irreps_in=conv_irreps_out,
act=acts[nonlin_scalars[1]],
normalize=True,
epsilon=1e-8,
bias=False,
dtype=dtype,
ncon_dtype=ncon_dtype
)

conv = Convolution(
irreps_node_input=irreps_node,
irreps_node_attr=self.irreps_node_attr,
irreps_node_output=conv_irreps_out,
irreps_edge_attr=self.irreps_edge_attr,
irreps_edge_scalars=self.irreps_edge_scalars,
**convolution_kwargs,
dtype=dtype,
ncon_dtype=ncon_dtype
)
irreps_node = nonlinear.irreps_out

self.layers.append(Compose(conv, nonlinear))

if irreps_prev == irreps_node and resnet:
self.resnets.append(True)
else:
self.resnets.append(False)
irreps_prev = irreps_node

def construct(self, node_input, node_attr, edge_src, edge_dst, edge_attr, edge_scalars):
"""construct"""
layer_in = node_input
for i in enumerate(self.layers):
layer_out = self.layers[i](
layer_in, node_attr, edge_src, edge_dst, edge_attr, edge_scalars)

if self.resnets[i]:
layer_in = layer_out + layer_in
else:
layer_in = layer_out
return layer_in

+ 0
- 141
MindChem/applications/matformer/mindchemistry/cell/nequip.py View File

@@ -1,141 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""network"""
from mindspore import nn, float32, int32
from mindscience.e3nn.o3 import Irreps, SphericalHarmonics, Linear
from mindscience.e3nn.nn import OneHot
from mindscience.e3nn.utils import radius_graph
from ..graph.graph import AggregateNodeToGlobal
from .message_passing import MessagePassing
from .embedding import RadialEdgeEmbedding


class AtomwiseLinear(nn.Cell):
"""AtomwiseLinear"""

def __init__(self, irreps_in, irreps_out, dtype=float32, ncon_dtype=float32):
super().__init__()
self.irreps_in = Irreps(irreps_in)
self.irreps_out = Irreps(irreps_out)
self.linear = Linear(self.irreps_in, self.irreps_out, dtype=dtype, ncon_dtype=ncon_dtype)

def construct(self, node_input):
return self.linear(node_input)

def __repr__(self):
return self.linear.__repr__()


class Nequip(nn.Cell):
"""EnergyNet"""

def __init__(
self,
irreps_embedding_out,
irreps_conv_out='16x0e',
chemical_embedding_irreps_out='64x0e',
r_max=4.0,
num_layers=3,
num_type=4,
num_basis=8,
cutoff_p=6,
hidden_mul=50,
lmax=2,
pred_force=False,
dtype=float32,
ncon_dtype=float32
):
super().__init__()
self.r_max = r_max
self.irreps_conv_out = Irreps(irreps_conv_out)
self.pred_force = pred_force
self.irreps_embedding_out = Irreps(irreps_embedding_out)
if pred_force:
self.irreps_embedding_out += Irreps([(self.irreps_embedding_out.data[0].mul, (1, -1))])

irreps_node_hidden = Irreps([(hidden_mul, (l, p))
for l in range(lmax + 1) for p in [-1, 1]])

self.one_hot = OneHot(num_type, dtype=dtype)
self.sh = SphericalHarmonics(range(lmax + 1), True, normalization="component", dtype=dtype)
self.radial_embedding = RadialEdgeEmbedding(r_max, num_basis, cutoff_p, dtype=dtype)

irreps_output = Irreps(chemical_embedding_irreps_out)
self.lin_input = AtomwiseLinear(self.one_hot.irreps_output, irreps_output, dtype=dtype, ncon_dtype=ncon_dtype)

irreps_edge_scalars = self.radial_embedding.irreps_out

irrep_node_features = irreps_output

self.mp = MessagePassing(
irreps_node_input=irrep_node_features,
irreps_node_attr=self.one_hot.irreps_output,
irreps_node_hidden=irreps_node_hidden,
irreps_node_output=self.irreps_conv_out,
irreps_edge_attr=self.sh.irreps_out,
irreps_edge_scalars=irreps_edge_scalars,
num_layers=num_layers,
resnet=False,
convolution_kwargs={'invariant_layers': 3, 'invariant_neurons': 64, 'avg_num_neighbors': 9,
'nonlin_scalars': {"e": "silu"}},
nonlin_scalars={"e": "silu", "o": "tanh"},
nonlin_gates={"e": "silu", "o": "tanh"},
dtype=dtype,
ncon_dtype=ncon_dtype
)
self.lin1 = AtomwiseLinear(self.irreps_conv_out, self.irreps_embedding_out, dtype=dtype, ncon_dtype=ncon_dtype)

irreps_out = '1x0e+1x1o' if pred_force else '1x0e'

self.lin2 = AtomwiseLinear(self.irreps_embedding_out, irreps_out, dtype=dtype, ncon_dtype=ncon_dtype)

self.scatter = AggregateNodeToGlobal()

def preprocess(self, data):
"""preprocess"""
if "batch" in data:
batch = data["batch"]
else:
batch = data["pos"].new_zeros(data["pos"].shape[0], dtype=int32)

edge_index = radius_graph(
data["pos"], self.r_max, batch, max_num_neighbors=len(data["pos"]) - 1)
edge_src = edge_index[0]
edge_dst = edge_index[1]

return batch, edge_src, edge_dst

def construct(self, batch, atom_type, atom_pos, edge_src, edge_dst, batch_size):
"""construct"""
edge_vec = atom_pos[edge_dst] - atom_pos[edge_src]
node_inputs = self.one_hot(atom_type)
node_attr = node_inputs.copy()
edge_attr = self.sh(edge_vec)

edge_length = edge_vec.norm(None, 1)
edge_length_embedding = self.radial_embedding(edge_length)

node_features = self.lin_input(node_inputs)
node_features = self.mp(node_features, node_attr, edge_src, edge_dst, edge_attr, edge_length_embedding)
node_features = self.lin1(node_features)
node_features = self.lin2(node_features)

if self.pred_force:
energy = self.scatter(node_attr=node_features[:, :1], batch=batch, dim_size=batch_size)
forces = node_features[:, 1:]
return energy, forces

energy = self.scatter(node_attr=node_features, batch=batch, dim_size=batch_size)
return energy

+ 0
- 15
MindChem/applications/matformer/mindchemistry/graph/__init__.py View File

@@ -1,15 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""graph"""

+ 0
- 19
MindChem/applications/matformer/mindchemistry/so2_conv/__init__.py View File

@@ -1,19 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
init file
"""
from .so3 import SO3Rotation
from .so2 import SO2Convolution

+ 0
- 64
MindChem/applications/matformer/mindchemistry/so2_conv/init_edge_rot_mat.py View File

@@ -1,64 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
file to get rotating matrix from edge distance vector
"""
from mindspore import ops
import mindspore.numpy as ms_np


def init_edge_rot_mat(edge_distance_vec):
"""
get rotating matrix from edge distance vector
"""
epsilon = 0.00000001
edge_vec_0 = edge_distance_vec
edge_vec_0_distance = ops.sqrt(ops.maximum(ops.sum(edge_vec_0 ** 2, dim=1), epsilon))
# Make sure the atoms are far enough apart
norm_x = ops.div(edge_vec_0, edge_vec_0_distance.view(-1, 1))
edge_vec_2 = ops.rand_like(edge_vec_0) - 0.5

edge_vec_2 = ops.div(edge_vec_2, ops.sqrt(ops.maximum(ops.sum(edge_vec_2 ** 2, dim=1), epsilon)).view(-1, 1))
# Create two rotated copies of the random vectors in case the random vector is aligned with norm_x
# With two 90 degree rotated vectors, at least one should not be aligned with norm_x
edge_vec_2b = edge_vec_2.copy()
edge_vec_2b[:, 0] = -edge_vec_2[:, 1]
edge_vec_2b[:, 1] = edge_vec_2[:, 0]
edge_vec_2c = edge_vec_2.copy()
edge_vec_2c[:, 1] = -edge_vec_2[:, 2]
edge_vec_2c[:, 2] = edge_vec_2[:, 1]
vec_dot_b = ops.abs(ops.sum(edge_vec_2b * norm_x, dim=1)).view(-1, 1)
vec_dot_c = ops.abs(ops.sum(edge_vec_2c * norm_x, dim=1)).view(-1, 1)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1)).view(-1, 1)
edge_vec_2 = ops.where(ops.broadcast_to(ops.gt(vec_dot, vec_dot_b), edge_vec_2b.shape), edge_vec_2b, edge_vec_2)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1)).view(-1, 1)
edge_vec_2 = ops.where(ops.broadcast_to(ops.gt(vec_dot, vec_dot_c), edge_vec_2c.shape), edge_vec_2c, edge_vec_2)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1))
# Check the vectors aren't aligned

norm_z = ms_np.cross(norm_x, edge_vec_2, axis=1)
norm_z = ops.div(norm_z, ops.sqrt(ops.maximum(ops.sum(norm_z ** 2, dim=1, keepdim=True), epsilon)))
norm_z = ops.div(norm_z, ops.sqrt(ops.maximum(ops.sum(norm_z ** 2, dim=1), epsilon)).view(-1, 1))

norm_y = ms_np.cross(norm_x, norm_z, axis=1)
norm_y = ops.div(norm_y, ops.sqrt(ops.maximum(ops.sum(norm_y ** 2, dim=1, keepdim=True), epsilon)))
# Construct the 3D rotation matrix
norm_x = norm_x.view(-1, 3, 1)
norm_y = -norm_y.view(-1, 3, 1)
norm_z = norm_z.view(-1, 3, 1)
edge_rot_mat_inv = ops.cat([norm_z, norm_x, norm_y], axis=2)

edge_rot_mat = ops.swapaxes(edge_rot_mat_inv, 1, 2)
return edge_rot_mat

BIN
MindChem/applications/matformer/mindchemistry/so2_conv/jd.pkl View File


+ 0
- 244
MindChem/applications/matformer/mindchemistry/so2_conv/so2.py View File

@@ -1,244 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
so2 file
"""
import mindspore as ms
from mindspore import ops, nn
from mindscience.e3nn.o3 import Irreps


class SO2MConvolution(nn.Cell):
"""
SO2 Convolution subnetwork
"""

def __init__(self, in_channels, out_channels):
super().__init__()
self.fc = nn.Dense(in_channels // 2, out_channels,
has_bias=False).to_float(ms.float16)
self.out_channels = out_channels

def construct(self, x_m):
"""
SO2 Convolution sub network construct process
"""
x_m = self.fc(x_m).astype(ms.float32)
x_i = ops.narrow(x_m, 2, 0, self.out_channels // 2)
x_r = ops.narrow(x_m, 2, self.out_channels // 2,
self.out_channels // 2)

x_m_r = ops.narrow(x_r, 1, 1, 1) - ops.narrow(
x_i, 1, 0, 1) # x_r[:, 1] - x_i[:, 0]
x_m_i = ops.narrow(x_i, 1, 1, 1) + ops.narrow(
x_r, 1, 0, 1) # x_i[:, 1] + x_r[:, 0]

x_out = ops.cat((x_m_i, x_m_r), axis=1)
return x_out


class SO2Convolution(nn.Cell):
"""
SO2 Convolution network
"""

def __init__(self, irreps_in, irreps_out):
super().__init__()

self.irreps_in1 = Irreps(irreps_in)
self.irreps_out = Irreps(irreps_out)

self.max_order_in = -1
for mulir in self.irreps_in1:
self.max_order_in = max(self.max_order_in, mulir.ir.l)
self.max_order_out = -1
for mulir in self.irreps_out:
self.max_order_out = max(self.max_order_out, mulir.ir.l)

self.m_shape_dict_in, self.irreps_in1_length = self.get_m_info(
self.irreps_in1, self.max_order_in)
self.m_shape_dict_out, self.irreps_out_length = self.get_m_info(
self.irreps_out, self.max_order_out)

self.fc_m0 = nn.Dense(self.m_shape_dict_in.get(0, None),
self.m_shape_dict_out.get(0, None)).to_float(ms.float16)

self.global_max_order = min(self.max_order_in + 1,
self.max_order_out + 1)

self.so2_m_conv = nn.CellList([])
for i in range(self.global_max_order):
if i == 0:
continue
so2_m_convolution = SO2MConvolution(self.m_shape_dict_in.get(i, None),
self.m_shape_dict_out.get(i, None))
self.so2_m_conv.append(so2_m_convolution)

self.max_m_in = 2 * self.max_order_in + 1

self.irreps_out_data = []
for mulir in self.irreps_out:
key = mulir.ir.l
value = mulir.mul
self.irreps_out_data.append((key, value))

def get_m_info(self, irreps, max_order):
"""
helper function to get m_info
"""
m_shape_dict = {}
m_mul_ir_l_dict = {}
for i in range(max_order + 1):
m_shape_dict[i] = 0

for mulir in irreps:
mul = mulir.mul
ir_l = mulir.ir.l
if ir_l not in m_mul_ir_l_dict:
m_mul_ir_l_dict[ir_l] = mul
else:
m_mul_ir_l_dict[ir_l] = m_mul_ir_l_dict[ir_l] + mul
for j in range(mulir.ir.l + 1):
if j == 0:
m_shape_dict[j] = m_shape_dict[j] + mul
else:
m_shape_dict[j] = m_shape_dict[j] + 2 * mul

return m_shape_dict, len(irreps)

def get_m_list_merge(self, x):
"""
helper function to get m_list_merge
"""
m_list = []
for _ in range(self.max_m_in):
m_list.append([])

index_shifting = int((self.max_m_in - 1) / 2)
for tmp in x:
m_length = tmp.shape[-1]
m_shift = int((m_length - 1) / 2)
for j in range(m_length):
m_list[j - m_shift + index_shifting].append(tmp[:, :, j])

m_list_merge = []
for i in range(index_shifting + 1):
if i == 0:
m_list_merge.append(ops.cat(m_list[index_shifting - i], -1))
else:
m_list_merge.append(
ops.cat((ops.cat(m_list[index_shifting - i], -1),
ops.cat(m_list[index_shifting + i], -1)), -1))
return m_list_merge

def construct(self, x, x_edge):
"""
SO2 Convolution network construct process
"""
##################### _m_primary #########################
num_edges = ops.shape(x_edge)[0]
m_list_merge = self.get_m_list_merge(x)
# ##################### finish _m_primary #########################
# radial function
out = []

### Compute m=0 coefficients separately since they only have real values
x_0 = m_list_merge[0]

x_0 = self.fc_m0(x_0).astype(ms.float32)
out.append(x_0)

#### Compute the values for the m > 0 coefficients
for m in range(self.global_max_order):
if m == 0:
continue
x_m = m_list_merge[m]
x_m = x_m.reshape(num_edges, 2, -1)
x_m = self.so2_m_conv[m - 1](x_m)
out.append(x_m)

###################### start fill 0 ######################
if self.max_order_out + 1 > len(m_list_merge):
for m in range(len(m_list_merge), self.max_order_out + 1):
extra_zero = ops.zeros(
(num_edges, 2, int(self.m_shape_dict_out.get(m, None) / 2)))
out.append(extra_zero)
###################### finish fill 0 ######################

###################### start _l_primary #########################
l_primary_list_0 = []
l_primary_list_left = []
l_primary_list_right = []

for _ in range(self.irreps_out_length):
l_primary_list_0.append([])
l_primary_list_left.append([])
l_primary_list_right.append([])

m_0 = out[0]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= 0:
l_primary_list_0[index].append(
ops.unsqueeze(m_0[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

for m in range(1, len(out)):
right = out[m][:, 1]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= m:
l_primary_list_right[index].append(
ops.unsqueeze(right[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

for m in range(len(out) - 1, 0, -1):
left = out[m][:, 0]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= m:
l_primary_list_left[index].append(
ops.unsqueeze(left[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

l_primary_list = []
for i in range(self.irreps_out_length):
if i == 0:
tmp = ops.cat(l_primary_list_0[i], -1)
l_primary_list.append(tmp)
else:
tmp = ops.cat(
(ops.cat((ops.cat(l_primary_list_left[i],
-1), ops.cat(l_primary_list_0[i], -1)),
-1), ops.cat(l_primary_list_right[i], -1)), -1)
l_primary_list.append(tmp)

##################### finish _l_primary #########################
return tuple(l_primary_list)

+ 0
- 156
MindChem/applications/matformer/mindchemistry/so2_conv/so3.py View File

@@ -1,156 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
so3 file
"""
import mindspore as ms
from mindspore import nn, ops, vmap, jit_class
from mindspore.numpy import tensordot
from mindscience.e3nn import o3
from mindscience.e3nn.o3 import Irreps

from .wigner import wigner_D


class SO3Embedding(nn.Cell):
"""
SO3Embedding class
"""

def __init__(self):
self.embedding = None

def _rotate(self, so3rotation, lmax_list, max_list):
"""
SO3Embedding rotate
"""
embedding_rotate = so3rotation[0].rotate(self.embedding, lmax_list[0],
max_list[0])
self.embedding = embedding_rotate

def _rotate_inv(self, so3rotation):
"""
SO3Embedding rotate inverse
"""
embedding_rotate = so3rotation[0].rotate_inv(self.embedding,
self.lmax_list[0],
self.mmax_list[0])
self.embedding = embedding_rotate


@jit_class
class SO3Rotation:
"""
SO3_Rotation class
"""

def __init__(self, lmax, irreps_in, irreps_out):
self.lmax = lmax
self.irreps_in1 = Irreps(irreps_in)
self.irreps_out = Irreps(irreps_out)
self.tensordot_vmap = vmap(tensordot, (0, 0, None), 0)

@staticmethod
def narrow(inputs, axis, start, length):
"""
SO3_Rotation narrow class
"""
begins = [0] * inputs.ndim
begins[axis] = start

sizes = list(inputs.shape)

sizes[axis] = length
res = ops.slice(inputs, begins, sizes)
return res

@staticmethod
def rotation_to_wigner_d_matrix(edge_rot_mat, start_lmax, end_lmax):
"""
SO3_Rotation rotation_to_wigner_d_matrix
"""
x = edge_rot_mat @ ms.Tensor([0.0, 1.0, 0.0])
alpha, beta = o3.xyz_to_angles(x)
rvalue = (ops.swapaxes(
o3.angles_to_matrix(alpha, beta, ops.zeros_like(alpha)), -1, -2)
@ edge_rot_mat)
gamma = ops.atan2(rvalue[..., 0, 2], rvalue[..., 0, 0])

block_list = []
for lmax in range(start_lmax, end_lmax + 1):
block = wigner_D(lmax, alpha, beta, gamma).astype(ms.float32)
block_list.append(block)
return block_list

def set_wigner(self, rot_mat3x3):
"""
SO3_Rotation set_wigner
"""
wigner = self.rotation_to_wigner_d_matrix(rot_mat3x3, 0, self.lmax)
wigner_inv = []
length = len(wigner)
for i in range(length):
wigner_inv.append(ops.swapaxes(wigner[i], 1, 2))
return tuple(wigner), tuple(wigner_inv)

def rotate(self, embedding, wigner):
"""
SO3_Rotation rotate
"""
res = []
batch_shape = embedding.shape[:-1]
for (s, l), mir in zip(self.irreps_in1.slice_tuples,
self.irreps_in1.data):
v_slice = self.narrow(embedding, -1, s, l)
if embedding.ndim == 1:
res.append((v_slice.reshape((1,) + batch_shape +
(mir.mul, mir.ir.dim)), mir.ir))
else:
res.append(
(v_slice.reshape(batch_shape + (mir.mul, mir.ir.dim)),
mir.ir))
rotate_data_list = []
for data, ir in res:
self.tensordot_vmap(data.astype(ms.float16),
wigner[ir.l].astype(ms.float16), ([1], [1]))
rotate_data = self.tensordot_vmap(data.astype(ms.float16),
wigner[ir.l].astype(ms.float16),
((1), (1))).astype(ms.float32)
rotate_data_list.append(rotate_data)
return tuple(rotate_data_list)

def rotate_inv(self, embedding, wigner_inv):
"""
SO3_Rotation rotate_inv
"""
res = []
batch_shape = embedding[0].shape[0:1]
index = 0
for (_, _), mir in zip(self.irreps_out.slice_tuples,
self.irreps_out.data):
v_slice = embedding[index]
if embedding[0].ndim == 1:
res.append((v_slice, mir.ir))
else:
res.append((v_slice, mir.ir))
index = index + 1
rotate_back_data_list = []
for data, ir in res:
rotate_back_data = self.tensordot_vmap(
data.astype(ms.float16), wigner_inv[ir.l].astype(ms.float16),
((1), (1))).astype(ms.float32)
rotate_back_data_list.append(
rotate_back_data.view(batch_shape + (-1,)))
return ops.cat(rotate_back_data_list, -1)

+ 0
- 61
MindChem/applications/matformer/mindchemistry/so2_conv/wigner.py View File

@@ -1,61 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
wigner file
"""

# pylint: disable=C0103
import pickle
from mindspore import ops
import mindspore as ms
from mindscience.e3nn.utils.func import broadcast_args


def wigner_D(lv, alpha, beta, gamma):
"""
# Borrowed from e3nn @ 0.4.0:
# https://github.com/e3nn/e3nn/blob/0.4.0/e3nn/o3/_wigner.py#L10
# jd is a list of tensors of shape (2l+1, 2l+1)

# Borrowed from e3nn @ 0.4.0:
# https://github.com/e3nn/e3nn/blob/0.4.0/e3nn/o3/_wigner.py#L37
#
# In 0.5.0, e3nn shifted to torch.matrix_exp which is significantly slower:
# https://github.com/e3nn/e3nn/blob/0.5.0/e3nn/o3/_wigner.py#L92
"""
jd = None
with open("jd.pkl", "rb") as f:
jd = pickle.load(f)
if not lv < len(jd):
raise NotImplementedError(
f"wigner D maximum l implemented is {len(jd) - 1}, send us an email to ask for more"
)
alpha, beta, gamma = broadcast_args(alpha, beta, gamma)
j = jd[lv]
xa = _z_rot_mat(alpha, lv)
xb = _z_rot_mat(beta, lv)
xc = _z_rot_mat(gamma, lv)
return xa @ j.astype(ms.float16) @ xb @ j.astype(ms.float16) @ xc


def _z_rot_mat(angle, lv):
shape = angle.shape
m = ops.zeros((shape[0], 2 * lv + 1, 2 * lv + 1))
inds = ops.arange(0, 2 * lv + 1, 1)
reversed_inds = ops.arange(2 * lv, -1, -1)
frequencies = ops.arange(lv, -lv - 1, -1)
m[..., inds, reversed_inds] = ops.sin(frequencies * angle[..., None])
m[..., inds, inds] = ops.cos(frequencies * angle[..., None])
return m.astype(ms.float16)

+ 0
- 18
MindChem/applications/matformer/mindchemistry/utils/__init__.py View File

@@ -1,18 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this filepio[] except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""init"""
from .load_config import load_yaml_config

__all__ = ['load_yaml_config']

+ 0
- 128
MindChem/applications/matformer/mindchemistry/utils/check_func.py View File

@@ -1,128 +0,0 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""functions"""
from __future__ import absolute_import

from mindspore import context

_SPACE = " "


def _convert_to_tuple(params):
if params is None:
return params
if not isinstance(params, (list, tuple)):
params = (params,)
if isinstance(params, list):
params_out = tuple(params)
else:
params_out = params # ✅ 防止未定义
return params_out


def check_param_type(param, param_name, data_type=None, exclude_type=None):
"""Check parameter's data type"""
data_type = _convert_to_tuple(data_type)
exclude_type = _convert_to_tuple(exclude_type)

if data_type and not isinstance(param, data_type):
raise TypeError(
f"The type of {param_name} should be instance of {data_type}, but got {param} with type {type(param)}"
)
if exclude_type and type(param) in exclude_type:
raise TypeError(
f"The type of {param_name} should not be instance of {exclude_type},but got {param} with type {type(param)}"
)


def check_param_value(param, param_name, valid_value):
"""check parameter's value"""
valid_value = _convert_to_tuple(valid_value)
if param not in valid_value:
raise ValueError(f"The value of {param_name} should be in {valid_value}, but got {param}")


def check_param_type_value(param, param_name, valid_value, data_type=None, exclude_type=None):
"""check both data type and value"""
check_param_type(param, param_name, data_type=data_type, exclude_type=exclude_type)
check_param_value(param, param_name, valid_value)


def check_dict_type(param_dict, param_name, key_type=None, value_type=None):
"""check data type for key and value of the specified dict"""
check_param_type(param_dict, param_name, data_type=dict)

for key in param_dict.keys():
if key_type:
check_param_type(key, _SPACE.join(("key of", param_name)), data_type=key_type)
if value_type:
values = _convert_to_tuple(param_dict[key])
for value in values:
check_param_type(value, _SPACE.join(("value of", param_name)), data_type=value_type)


def check_dict_value(param_dict, param_name, key_value=None, value_value=None):
"""check values for key and value of specified dict"""
check_param_type(param_dict, param_name, data_type=dict)

for key in param_dict.keys():
if key_value:
check_param_value(key, _SPACE.join(("key of", param_name)), key_value)
if value_value:
values = _convert_to_tuple(param_dict[key])
for value in values:
check_param_value(value, _SPACE.join(("value of", param_name)), value_value)


def check_dict_type_value(param_dict, param_name, key_type=None, value_type=None, key_value=None, value_value=None):
"""check values for key and value of specified dict"""
check_dict_type(param_dict, param_name, key_type=key_type, value_type=value_type)
check_dict_value(param_dict, param_name, key_value=key_value, value_value=value_value)


def check_mode(api_name):
"""check running mode"""
if context.get_context("mode") == context.PYNATIVE_MODE:
raise RuntimeError(f"{api_name} is only supported GRAPH_MODE now but got PYNATIVE_MODE")


def check_param_no_greater(param, param_name, compared_value):
""" Check whether the param less than the given compared_value"""
if param > compared_value:
raise ValueError(f"The value of {param_name} should be no greater than {compared_value}, but got {param}")


def check_param_odd(param, param_name):
""" Check whether the param is an odd number"""
if param % 2 == 0:
raise ValueError(f"The value of {param_name} should be an odd number, but got {param}")


def check_param_even(param, param_name):
""" Check whether the param is an even number"""
for value in param:
if value % 2 != 0:
raise ValueError(f"The value of {param_name} should be an even number, but got {param}")


def check_lr_param_type_value(param, param_name, param_type, thresh_hold=0, restrict=False, exclude=None):
if (exclude and isinstance(param, exclude)) or not isinstance(param, param_type):
raise TypeError(f"the type of {param_name} should be {param_type}, but got {type(param)}")
if restrict:
if param <= thresh_hold:
raise ValueError(f"the value of {param_name} should be > {thresh_hold}, but got: {param}")
else:
if param < thresh_hold:
raise ValueError(f"the value of {param_name} should be >= {thresh_hold}, but got: {param}")

+ 0
- 85
MindChem/applications/matformer/mindchemistry/utils/load_config.py View File

@@ -1,85 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
utility functions
"""
import os
import yaml


def _make_paths_absolute(dir_, config):
"""
Make all values for keys ending with `_path` absolute to dir_.

Args:
dir_ (str): The path of yaml configuration file.
config (dict): The yaml for configuration file.

Returns:
Dict. The configuration information in dict format.
"""
for key in config.keys():
if key.endswith("_path"):
config[key] = os.path.join(dir_, config[key])
config[key] = os.path.abspath(config[key])
if isinstance(config[key], dict):
config[key] = _make_paths_absolute(dir_, config[key])
return config


def load_yaml_config(file_path):
"""
Load a YAML configuration file.

Args:
file_path (str): The path of yaml configuration file.

Returns:
Dict. The configuration information in dict format.

Supported Platforms:
``Ascend`` ``CPU`` ``GPU``

Examples:
>>> from mindchemistry.utils import load_yaml_config
>>> config_file_path = 'xxx' # 'xxx' is the file_path
>>> configs = load_yaml_config(config_file_path)
"""
# Read YAML experiment definition file
with open(file_path, 'r', encoding='utf-8') as stream:
config = yaml.safe_load(stream)
config = _make_paths_absolute(os.path.join(
os.path.dirname(file_path), ".."), config)
return config


def load_yaml_config_from_path(file_path):
"""
Load a YAML configuration file.

Args:
file_path (str): The path of yaml configuration file.

Returns:
Dict. The configuration information in dict format.

Supported Platforms:
``Ascend`` ``CPU`` ``GPU``
"""
# Read YAML experiment definition file
with open(file_path, 'r', encoding='utf-8') as stream:
config = yaml.safe_load(stream)

return config

+ 20
- 0
MindChem/applications/matformer/models/__init__.py View File

@@ -0,0 +1,20 @@
"""
Matformer Model Package
-----------------------

This package defines the core neural network architectures used in the
Matformer application of MindChem.

Typical contents of this package include:
- Backbone definitions for the Matformer encoder.
- Embedding and positional encoding modules.
- Readout / property head for energy, force or other scalar targets.
- Utility functions for building models from config dictionaries.

The package is designed to be:
- Modular: different components (embedding, attention blocks, heads)
can be swapped or extended.
- Config-driven: model hyper-parameters are usually specified in
YAML / JSON config files and parsed into constructor arguments.
- MindSpore-friendly: all models are implemented with MindSpore
`nn.Cell`, supporting graph mode and Ascend devices."""

MindChem/applications/diffcsp/mindchemistry/graph/__init__.py → MindChem/applications/matformer/models/graph/__init__.py View File


MindChem/applications/matformer/mindchemistry/graph/dataloader.py → MindChem/applications/matformer/models/graph/dataloader.py View File


MindChem/applications/matformer/mindchemistry/graph/graph.py → MindChem/applications/matformer/models/graph/graph.py View File


MindChem/applications/nequip/mindchemistry/graph/loss.py → MindChem/applications/matformer/models/graph/loss.py View File


MindChem/applications/matformer/mindchemistry/graph/normlization.py → MindChem/applications/matformer/models/graph/normlization.py View File


MindChem/applications/nequip/mindchemistry/cell/matformer/matformer.py → MindChem/applications/matformer/models/matformer.py View File

@@ -15,9 +15,9 @@
"""Matformer"""
from mindspore import nn, ops
import mindspore as ms
from mindchemistry.graph.graph import AggregateNodeToGlobal
from mindchemistry.cell.matformer.utils import RBFExpansion
from mindchemistry.cell.matformer.transformer import MatformerConv, Silu
from models.graph.graph import AggregateNodeToGlobal
from models.utils import RBFExpansion
from models.transformer import MatformerConv, Silu


class Matformer(nn.Cell):

MindChem/applications/nequip/mindchemistry/cell/matformer/transformer.py → MindChem/applications/matformer/models/transformer.py View File

@@ -15,8 +15,8 @@
"""transformer file"""
import mindspore as ms
from mindspore import ops, Tensor, nn
from mindchemistry.graph.graph import LiftNodeToEdge, AggregateEdgeToNode
from mindchemistry.graph.normlization import BatchNormMask
from models.graph.graph import LiftNodeToEdge, AggregateEdgeToNode
from models.graph.normlization import BatchNormMask


class Silu(nn.Cell):

MindChem/applications/matformer/mindchemistry/cell/matformer/utils.py → MindChem/applications/matformer/models/utils.py View File


+ 4
- 4
MindChem/applications/matformer/predict.py View File

@@ -23,10 +23,10 @@ import numpy as np
import mindspore as ms
from mindspore import set_seed
from data.generate import get_prop_model
from mindchemistry.cell.matformer.matformer import Matformer
from mindchemistry.cell.matformer.utils import LossRecord
from mindchemistry.graph.loss import L1LossMask, L2LossMask
from mindchemistry.graph.dataloader import DataLoaderBase as DataLoader
from models.matformer import Matformer
from models.utils import LossRecord
from models.graph.loss import L1LossMask, L2LossMask
from models.graph.dataloader import DataLoaderBase as DataLoader

logging.basicConfig(level=logging.INFO)



+ 4
- 4
MindChem/applications/matformer/train.py View File

@@ -24,10 +24,10 @@ import mindspore as ms
from mindspore import nn, set_seed
from mindspore.amp import all_finite
from data.generate import get_prop_model
from mindchemistry.cell.matformer.matformer import Matformer
from mindchemistry.cell.matformer.utils import LossRecord, OneCycleLr
from mindchemistry.graph.loss import L1LossMask, L2LossMask
from mindchemistry.graph.dataloader import DataLoaderBase as DataLoader
from models.matformer import Matformer
from models.utils import LossRecord, OneCycleLr
from models.graph.loss import L1LossMask, L2LossMask
from models.graph.dataloader import DataLoaderBase as DataLoader

logging.basicConfig(level=logging.INFO)



+ 0
- 64
MindChem/applications/nequip/mindchemistry/__init__.py View File

@@ -1,64 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Initialization for MindChemistry APIs."""

import time
import mindspore as ms
from mindspore import log as logger
from mindscience.e3nn import *
from .cell import *
from .utils import *
from .graph import *
from .so2_conv import *

__all__ = []
__all__.extend(cell.__all__)
__all__.extend(utils.__all__)

def _mindspore_version_check():
"""
Check MindSpore version for MindChemistry.

Raises:
ImportError: If MindSpore cannot be imported.
"""
try:
_ = ms.__version__
except ImportError as exc:
raise ImportError(
"Cannot find MindSpore in the current environment. Please install "
"MindSpore before using MindChemistry, by following the instruction at "
"https://www.mindspore.cn/install"
) from exc

ms_version = ms.__version__[:5]
required_mindspore_version = "1.8.1"

if ms_version < required_mindspore_version:
logger.warning(
f"Current version of MindSpore ({ms_version}) is not compatible with MindChemistry. "
f"Some functions might not work or even raise errors. Please install MindSpore "
f"version >= {required_mindspore_version}. For more details about dependency settings, "
f"please check the instructions at the MindSpore official website "
f"https://www.mindspore.cn/install or check the README.md at "
f"https://gitee.com/mindspore/mindscience"
)

for i in range(3, 0, -1):
logger.warning(f"Please pay attention to the above warning, countdown: {i}")
time.sleep(1)


_mindspore_version_check()

+ 0
- 24
MindChem/applications/nequip/mindchemistry/cell/__init__.py View File

@@ -1,24 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""initialization for cells"""

from .nequip import Nequip
from .basic_block import AutoEncoder, FCNet, MLPNet
from .matformer import *

__all__ = [
"Nequip"
]
__all__.extend(matformer.__all__)

+ 0
- 38
MindChem/applications/nequip/mindchemistry/cell/activation.py View File

@@ -1,38 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""get activation function."""
from __future__ import absolute_import

from mindspore import ops
from mindspore.nn.layer import activation

_activation = {
'softmax': activation.Softmax,
'logsoftmax': activation.LogSoftmax,
'relu': activation.ReLU,
'silu': activation.SiLU,
'relu6': activation.ReLU6,
'tanh': activation.Tanh,
'gelu': activation.GELU,
'fast_gelu': activation.FastGelu,
'elu': activation.ELU,
'sigmoid': activation.Sigmoid,
'prelu': activation.PReLU,
'leakyrelu': activation.LeakyReLU,
'hswish': activation.HSwish,
'hsigmoid': activation.HSigmoid,
'logsigmoid': activation.LogSigmoid,
'sin': ops.Sin
}

+ 0
- 600
MindChem/applications/nequip/mindchemistry/cell/basic_block.py View File

@@ -1,600 +0,0 @@
# Copyright 2023 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""basic"""
from __future__ import absolute_import

from collections.abc import Sequence
from typing import Union

from mindspore import nn
from mindspore.nn.layer import activation
from mindspore import ops, float16, float32, Tensor
from mindspore.common.initializer import Initializer

from .activation import _activation


def _get_dropout(dropout_rate):
"""
Gets the dropout functions.

Inputs:
dropout_rate (Union[int, float]): The dropout rate of the dropout function.
If dropout_rate was int or not in range (0,1], it would be rectify to closest float value.

Returns:
Function, the dropout function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_dropout
>>> dropout = get_dropout(0.5)
>>> dropout.set_train
Dropout<keep_prob=0.5>
"""
dropout_rate = float(max(min(dropout_rate, 1.), 1e-7))
return nn.Dropout(keep_prob=dropout_rate)


def _get_layernorm(channel, epsilon):
"""
Gets the layer normalization functions.

Inputs:
channel (Union[int, list]): The normalized shape of the layer normalization function.
If channel was int, it would be wrap into a list.
epsilon (float): The epsilon of the layer normalization function.

Returns:
Function, the layer normalization function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layernorm
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> layernorm = get_layernorm([2], 1e-7)
>>> output = layernorm(input_x)
>>> print(output)
[[ 9.99999881e-01, -9.99999881e-01],
[-1.00000000e+00, 1.00000000e+00]]
"""
if isinstance(channel, int):
channel = [channel]
return nn.LayerNorm(channel, epsilon=epsilon)


def _get_activation(name):
"""
Gets the activation function.

Inputs:
name (Union[str, None]): The name of the activation function. If name was None, it would return [].

Returns:
Function, the activation function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_activation
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> sigmoid = _get_activation('sigmoid')
>>> output = sigmoid(input_x)
>>> print(output)
[[0.7685248 0.5249792 ]
[0.54983395 0.96083426]]
"""
if name is None:
return []
if isinstance(name, str):
name = name.lower()
if name not in _activation:
return activation.get_activation(name)
return _activation.get(name)()
return name


def _get_layer_arg(arguments, index):
"""
Gets the argument of each network layers.

Inputs:
arguments (Union[str, int, float, List, None]): The arguments of each layers.
If arguments was List return the argument at the index of the List.
index (int): The index of layer in the network

Returns:
Argument of the indexed layer.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = _get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = _get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
if isinstance(arguments, list):
if len(arguments) <= index:
if len(arguments) == 1:
return [] if arguments[0] is None else arguments[0]
return []
return [] if arguments[index] is None else arguments[index]
return [] if arguments is None else arguments


def get_linear_block(
in_channels,
out_channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
"""
Gets the linear block list.

Inputs:
in_channels (int): The number of input channel.
out_channels (int): The number of output channel.
weight_init (Union[str, float, mindspore.common.initializer]): The initializer of the weights of dense layer
has_bias (bool): The switch for whether dense layer has bias.
bias_init (Union[str, float, mindspore.common.initializer]): The initializer of the bias of dense layer
has_dropout (bool): The switch for whether linear block has a dropout layer.
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
has_layernorm (bool): The switch for whether linear block has a layer normalization layer.
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
has_activation (bool): The switch for whether linear block has an activation layer.
act (Union[str, None]): The activation function in linear block

Returns:
List of mindspore.nn.Cell, linear block list .

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
dense = nn.Dense(
in_channels, out_channels, weight_init=weight_init, bias_init=bias_init, has_bias=has_bias, activation=None
)
dropout = _get_dropout(dropout_rate) if (has_dropout is True) else []
layernorm = _get_layernorm(out_channels, layernorm_epsilon) if (has_layernorm is True) else []
act = _get_activation(act) if (has_activation is True) else []
block_list = [dense, dropout, layernorm, act]
while [] in block_list:
block_list.remove([])
return block_list


class FCNet(nn.Cell):
r"""
The Fully Connected Network. Applies a series of fully connected layers to the incoming data.

Args:
channels (List): the list of numbers of channel of each fully connected layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = FCNet([3, 16, 32, 16, 8])
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.has_bias = has_bias
self.bias_init = bias_init
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.layernorm_epsilon = layernorm_epsilon
self.has_activation = has_activation
self.activation = act
self.network = nn.SequentialCell(self._create_network())

def _create_network(self):
""" create the network """
cell_list = []
for i in range(len(self.channels) - 1):
cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return cell_list

def construct(self, x):
return self.network(x)


class MLPNet(nn.Cell):
r"""
The MLPNet Network. Applies a series of fully connected layers to the incoming data among which hidden layers have
same number of channels.

Args:
in_channels (int): the number of input layer channel.
out_channels (int): the number of output layer channel.
layers (int): the number of layers.
neurons (int): the number of channels of hidden layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1] .
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = MLPNet(in_channels=3, out_channels=8, layers=5, neurons=32)
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
in_channels,
out_channels,
layers,
neurons,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = (in_channels,) + (layers - 2) * \
(neurons,) + (out_channels,)
self.network = FCNet(
channels=self.channels,
weight_init=weight_init,
has_bias=has_bias,
bias_init=bias_init,
has_dropout=has_dropout,
dropout_rate=dropout_rate,
has_layernorm=has_layernorm,
layernorm_epsilon=layernorm_epsilon,
has_activation=has_activation,
act=act
)

def construct(self, x):
return self.network(x)


class MLPMixPrecision(nn.Cell):
"""MLPMixPrecision
"""

def __init__(
self,
input_dim: int,
hidden_dims: Sequence,
short_cut=False,
batch_norm=False,
activation_fn='relu',
has_bias=False,
weight_init: Union[Initializer, str] = 'xavier_uniform',
bias_init: Union[Initializer, str] = 'zeros',
dropout=0,
dtype=float32
):
super().__init__()
self.dtype = dtype
self.div = ops.Div()

self.dims = [input_dim] + hidden_dims
self.short_cut = short_cut
self.nonlinear_const = 1.0
if isinstance(activation_fn, str):
self.activation = _activation.get(activation_fn)()
if activation_fn is not None and activation_fn == 'silu':
self.nonlinear_const = 1.679177
else:
self.activation = activation_fn
self.dropout = None
if dropout:
self.dropout = nn.Dropout(dropout)
fcs = [
nn.Dense(dim, self.dims[i + 1], weight_init=weight_init, bias_init=bias_init,
has_bias=has_bias).to_float(self.dtype) for i, dim in enumerate(self.dims[:-1])
]
self.layers = nn.CellList(fcs)
self.batch_norms = None
if batch_norm:
bns = [nn.BatchNorm1d(dim) for dim in self.dims[1:-1]]
self.batch_norms = nn.CellList(bns)

def construct(self, inputs):
"""construct

Args:
inputs: inputs

Returns:
inputs
"""
hidden = inputs
norm_from_last = 1.0
for i, layer in enumerate(self.layers):
sqrt_dim = ops.sqrt(Tensor(float(self.dims[i])))
layer_hidden = layer(hidden)
if self.dtype == float16:
layer_hidden = layer_hidden.astype(float16)
hidden = self.div(layer_hidden * norm_from_last, sqrt_dim)
norm_from_last = self.nonlinear_const
if i < len(self.layers) - 1:
if self.batch_norms is not None:
x = hidden.flatten(0, -2)
hidden = self.batch_norms[i](x).view_as(hidden)
if self.activation is not None:
hidden = self.activation(hidden)
if self.dropout is not None:
hidden = self.dropout(hidden)
if self.short_cut and hidden.shape == hidden.shape:
hidden += inputs
return hidden


class AutoEncoder(nn.Cell):
r"""
The AutoEncoder Network.
Applies an encoder to get the latent code and applies a decoder to get the reconstruct data.

Args:
channels (list): The number of channels of each encoder and decoder layer.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .
out_act (Union[None, str, mindspore.nn.Cell]): The activation function to output layer. Default: ``None`` .

Inputs:
- **x** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **latents** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.
- **x_recon** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry import AutoEncoder
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = AutoEncoder([3, 6, 2])
>>> output = net(inputs)
>>> print(output[0].shape, output[1].shape)
(2, 2) (2, 3)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu',
out_act=None
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.bias_init = bias_init
self.has_bias = has_bias
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.has_activation = has_activation
self.layernorm_epsilon = layernorm_epsilon
self.activation = act
self.output_activation = out_act
self.encoder = nn.SequentialCell(self._create_encoder())
self.decoder = nn.SequentialCell(self._create_decoder())

def _create_encoder(self):
""" create the network encoder """
encoder_cell_list = []
for i in range(len(self.channels) - 1):
encoder_cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return encoder_cell_list

def _create_decoder(self):
""" create the network decoder """
decoder_channels = self.channels[::-1]
decoder_weight_init = self.weight_init[::-1] if isinstance(self.weight_init, list) else self.weight_init
decoder_bias_init = self.bias_init[::-1] if isinstance(self.bias_init, list) else self.bias_init
decoder_cell_list = []
for i in range(len(decoder_channels) - 1):
decoder_cell_list += get_linear_block(
decoder_channels[i],
decoder_channels[i + 1],
weight_init=_get_layer_arg(decoder_weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(decoder_bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
if self.output_activation is not None:
decoder_cell_list.append(_get_activation(self.output_activation))
return decoder_cell_list

def encode(self, x):
return self.encoder(x)

def decode(self, z):
return self.decoder(z)

def construct(self, x):
latents = self.encode(x)
x_recon = self.decode(latents)
return x_recon, latents

+ 0
- 120
MindChem/applications/nequip/mindchemistry/cell/convolution.py View File

@@ -1,120 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""convolution"""
from mindspore import nn, ops, float32
from mindscience.e3nn.o3 import TensorProduct, Irreps, Linear
from mindscience.e3nn.nn import FullyConnectedNet
from ..graph.graph import AggregateEdgeToNode

softplus = ops.Softplus()


def shift_softplus(x):
return softplus(x) - 0.6931471805599453


def silu(x):
return x * ops.sigmoid(x)


class Convolution(nn.Cell):
r"""
InteractionBlock.

Args:
irreps_node_input: Input Features, default = None
irreps_node_attr: Nodes attribute irreps
irreps_node_output: Output irreps, in our case typically a single scalar
irreps_edge_attr: Edge attribute irreps
invariant_layers: Number of invariant layers, default = 1
invariant_neurons: Number of hidden neurons in invariant function, default = 8
avg_num_neighbors: Number of neighbors to divide by, default None => no normalization.
use_sc(bool): use self-connection or not
"""

def __init__(self,
irreps_node_input,
irreps_node_attr,
irreps_node_output,
irreps_edge_attr,
irreps_edge_scalars,
invariant_layers=1,
invariant_neurons=8,
avg_num_neighbors=None,
use_sc=True,
nonlin_scalars=None,
dtype=float32,
ncon_dtype=float32):
super().__init__()
self.avg_num_neighbors = avg_num_neighbors
self.use_sc = use_sc

self.irreps_node_input = Irreps(irreps_node_input)
self.irreps_node_attr = Irreps(irreps_node_attr)
self.irreps_node_output = Irreps(irreps_node_output)
self.irreps_edge_attr = Irreps(irreps_edge_attr)
self.irreps_edge_scalars = Irreps([(irreps_edge_scalars.num_irreps, (0, 1))])

self.lin1 = Linear(self.irreps_node_input, self.irreps_node_input, dtype=dtype, ncon_dtype=ncon_dtype)

tp = TensorProduct(self.irreps_node_input,
self.irreps_edge_attr,
self.irreps_node_output,
'merge',
weight_mode='custom',
dtype=dtype,
ncon_dtype=ncon_dtype)

self.fc = FullyConnectedNet([self.irreps_edge_scalars.num_irreps] + invariant_layers * [invariant_neurons] +
[tp.weight_numel], {
"ssp": shift_softplus,
"silu": ops.silu,
}.get(nonlin_scalars.get("e", None), None), dtype=dtype)

self.tp = tp
self.scatter = AggregateEdgeToNode(dim=1)

self.lin2 = Linear(tp.irreps_out.simplify(), self.irreps_node_output, dtype=dtype, ncon_dtype=ncon_dtype)

self.sc = None
if self.use_sc:
self.sc = TensorProduct(self.irreps_node_input,
self.irreps_node_attr,
self.irreps_node_output,
'connect',
dtype=dtype,
ncon_dtype=ncon_dtype)

def construct(self, node_input, node_attr, edge_src, edge_dst, edge_attr, edge_scalars):
"""Evaluate interaction Block with resnet"""
weight = self.fc(edge_scalars)

node_features = self.lin1(node_input)

edge_features = self.tp(node_features[edge_src], edge_attr, weight)

node_features = self.scatter(edge_attr=edge_features, edge_index=[edge_src, edge_dst],
dim_size=node_input.shape[0])

if self.avg_num_neighbors is not None:
node_features = node_features.div(self.avg_num_neighbors**0.5)

node_features = self.lin2(node_features)

if self.sc is not None:
sc = self.sc(node_input, node_attr)
node_features = node_features + sc

return node_features

+ 0
- 146
MindChem/applications/nequip/mindchemistry/cell/embedding.py View File

@@ -1,146 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""embedding
"""
import math

import numpy as np
from mindspore import nn, ops, Tensor, Parameter, float32

from mindscience.e3nn.o3 import Irreps


def _poly_cutoff(x, factor, p=6.0):
x = x * factor
out = 1.0
out = out - (((p + 1.0) * (p + 2.0) / 2.0) * ops.pow(x, p))
out = out + (p * (p + 2.0) * ops.pow(x, p + 1.0))
out = out - ((p * (p + 1.0) / 2) * ops.pow(x, p + 2.0))
return out * (x < 1.0)


class PolyCutoff(nn.Cell):

def __init__(self, r_max, p=6):
super().__init__()
self.p = float(p)
self._factor = 1.0 / float(r_max)

def construct(self, x):
return _poly_cutoff(x, self._factor, p=self.p)


class MaskPolynomialCutoff(nn.Cell):
"""MaskPolynomialCutoff
"""
_factor: float
p: float

def __init__(self, r_max: float, p: float = 6):
super().__init__()
self.p = float(p)
self._factor = 1.0 / float(r_max)
self.r_max = Tensor(r_max, dtype=float32)

self.cutoff = r_max

def construct(self, distance: Tensor, mask: Tensor = None):
decay = _poly_cutoff(distance, self._factor, p=self.p)

mask_lower = distance < self.cutoff
if mask is not None:
mask_lower &= mask

return decay, mask_lower


class BesselBasis(nn.Cell):
"""BesselBasis
"""

def __init__(self, r_max, num_basis=8, dtype=float32):
super().__init__()
self.r_max = r_max
self.num_basis = num_basis
self.prefactor = 2.0 / self.r_max
bessel_weights = Tensor(np.linspace(1., num_basis, num_basis) * math.pi, dtype=dtype)
self.bessel_weights = Parameter(bessel_weights)

def construct(self, x):
numerator = ops.sin(self.bessel_weights * x.unsqueeze(-1) / self.r_max)
return self.prefactor * (numerator / x.unsqueeze(-1))


class NormBesselBasis(nn.Cell):
"""NormBesselBasis
"""

def __init__(self, r_max, num_basis=8, norm_num=4000):
super().__init__()

self.basis = BesselBasis(r_max=r_max, num_basis=num_basis)
self.rs = Tensor(np.linspace(0.0, r_max, num=norm_num + 1), float32)[1:]

self.sqrt = ops.Sqrt()
self.sq = ops.Square()
self.div = ops.Div()

bessel_weights = Tensor(np.linspace(1.0, num_basis, num=num_basis), float32)

bessel_weights = bessel_weights * Tensor(math.pi, float32)
edge_length = self.rs
edge_length_unsqueeze = edge_length.unsqueeze(-1)
bessel_edge_length = bessel_weights * edge_length_unsqueeze
if r_max != 0:
bessel_edge_length = bessel_edge_length / r_max
prefactor = 2.0 / r_max
else:
raise ValueError
self.sin = ops.Sin()
numerator = self.sin(bessel_edge_length)
bs = prefactor * self.div(numerator, edge_length_unsqueeze)

basis_mean = Tensor(np.mean(bs.asnumpy(), axis=0), float32)
basis_std = self.sqrt(Tensor(np.mean(self.sq(bs - basis_mean).asnumpy(), 0), float32))
inv_std = ops.reciprocal(basis_std)

self.basis_mean = basis_mean
self.inv_std = inv_std

def construct(self, edge_length):
basis_length = self.basis(edge_length)
return (basis_length - self.basis_mean) * self.inv_std


class RadialEdgeEmbedding(nn.Cell):
"""RadialEdgeEmbedding
"""

def __init__(self, r_max, num_basis=8, p=6, dtype=float32):
super().__init__()
self.num_basis = num_basis
self.cutoff_p = p
self.basis = BesselBasis(r_max, num_basis, dtype=dtype)
self.cutoff = PolyCutoff(r_max, p)

self.irreps_out = Irreps([(self.basis.num_basis, (0, 1))])

def construct(self, edge_length):
edge_length_embedded = self.basis(edge_length) * self.cutoff(edge_length).unsqueeze(-1)
return edge_length_embedded

def __repr__(self):
return f'RadialEdgeEmbedding [num_basis: {self.num_basis}, cutoff_p: ' \
+ f'{self.cutoff_p}] ( -> {self.irreps_out} | {self.basis.num_basis} weights)'

+ 0
- 19
MindChem/applications/nequip/mindchemistry/cell/matformer/__init__.py View File

@@ -1,19 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""initialization for matformer"""

from .matformer import Matformer

__all__ = ['Matformer']

+ 0
- 15
MindChem/applications/nequip/mindchemistry/graph/__init__.py View File

@@ -1,15 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""graph"""

+ 0
- 408
MindChem/applications/nequip/mindchemistry/graph/dataloader.py View File

@@ -1,408 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""dataloader
"""
import random
import numpy as np
from mindspore import Tensor
import mindspore as ms


class DataLoaderBase:
r"""
DataLoader that stacks a batch of graph data to fixed-size Tensors

For specific dataset, usually the following functions should be customized to include different fields:
__init__, shuffle_action, __iter__

"""

def __init__(self,
batch_size,
edge_index,
label=None,
node_attr=None,
edge_attr=None,
padding_std_ratio=3.5,
dynamic_batch_size=True,
shuffle_dataset=True,
max_node=None,
max_edge=None):
self.batch_size = batch_size
self.edge_index = edge_index
self.index = 0
self.step = 0
self.padding_std_ratio = padding_std_ratio
self.batch_change_num = 0
self.batch_exceeding_num = 0
self.dynamic_batch_size = dynamic_batch_size
self.shuffle_dataset = shuffle_dataset

### can be customized to specific dataset
self.label = label
self.node_attr = node_attr
self.edge_attr = edge_attr
self.sample_num = len(self.node_attr)
batch_size_div = self.batch_size
if batch_size_div != 0:
self.step_num = int(self.sample_num / batch_size_div)
else:
raise ValueError

if dynamic_batch_size:
self.max_start_sample = self.sample_num
else:
self.max_start_sample = self.sample_num - self.batch_size + 1

self.set_global_max_node_edge_num(self.node_attr, self.edge_attr, max_node, max_edge, shuffle_dataset,
dynamic_batch_size)
#######

def __len__(self):
return self.sample_num

### example of generating data of each step, can be customized to specific dataset
def __iter__(self):
if self.shuffle_dataset:
self.shuffle()
else:
self.restart()

while self.index < self.max_start_sample:
# pylint: disable=W0612
edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num, batch_size \
= self.gen_common_data(self.node_attr, self.edge_attr)

### can be customized to generate different attributes or labels according to specific dataset
node_attr_step = self.gen_node_attr(self.node_attr, batch_size, node_num)
edge_attr_step = self.gen_edge_attr(self.edge_attr, batch_size, edge_num)
label_step = self.gen_global_attr(self.label, batch_size)

self.add_step_index(batch_size)

### make number to Tensor, if it is used as a Tensor in the network
node_num = Tensor(node_num)
batch_size = Tensor(batch_size)

yield node_attr_step, edge_attr_step, label_step, edge_index_step, node_batch_step, \
node_mask, edge_mask, node_num, batch_size

@staticmethod
def pad_zero_to_end(src, axis, zeros_len):
"""pad_zero_to_end"""
pad_shape = []
for i in range(src.ndim):
if i == axis:
pad_shape.append((0, zeros_len))
else:
pad_shape.append((0, 0))
return np.pad(src, pad_shape)

@staticmethod
def gen_mask(total_len, real_len):
"""gen_mask"""
mask = np.concatenate((np.full((real_len,), np.float32(1)), np.full((total_len - real_len,), np.float32(0))))
return mask

### example of computing global max length of node_attr and edge_attr, can be customized to specific dataset
def set_global_max_node_edge_num(self,
node_attr,
edge_attr,
max_node=None,
max_edge=None,
shuffle_dataset=True,
dynamic_batch_size=True):
"""set_global_max_node_edge_num

Args:
node_attr: node_attr
edge_attr: edge_attr
max_node: max_node. Defaults to None.
max_edge: max_edge. Defaults to None.
shuffle_dataset: shuffle_dataset. Defaults to True.
dynamic_batch_size: dynamic_batch_size. Defaults to True.

Raises:
ValueError: ValueError
"""
if not shuffle_dataset:
max_node_num, max_edge_num = self.get_max_node_edge_num(node_attr, edge_attr, dynamic_batch_size)
self.max_node_num_global = max_node_num if max_node is None else max(max_node, max_node_num)
self.max_edge_num_global = max_edge_num if max_edge is None else max(max_edge, max_edge_num)
return

sum_node = 0
sum_edge = 0
count = 0
max_node_single = 0
max_edge_single = 0
for step in range(self.sample_num):
node_len = len(node_attr[step])
edge_len = len(edge_attr[step])
sum_node += node_len
sum_edge += edge_len
max_node_single = max(max_node_single, node_len)
max_edge_single = max(max_edge_single, edge_len)
count += 1
if count != 0:
mean_node = sum_node / count
mean_edge = sum_edge / count
else:
raise ValueError

if max_node is not None and max_edge is not None:
if max_node < max_node_single:
raise ValueError(
f"the max_node {max_node} is less than the max length of a single sample {max_node_single}")
if max_edge < max_edge_single:
raise ValueError(
f"the max_edge {max_edge} is less than the max length of a single sample {max_edge_single}")

self.max_node_num_global = max_node
self.max_edge_num_global = max_edge
elif max_node is None and max_edge is None:
sum_node = 0
sum_edge = 0
for step in range(self.sample_num):
sum_node += (len(node_attr[step]) - mean_node) ** 2
sum_edge += (len(edge_attr[step]) - mean_edge) ** 2

if count != 0:
std_node = np.sqrt(sum_node / count)
std_edge = np.sqrt(sum_edge / count)
else:
raise ValueError

self.max_node_num_global = int(self.batch_size * mean_node +
self.padding_std_ratio * np.sqrt(self.batch_size) * std_node)
self.max_edge_num_global = int(self.batch_size * mean_edge +
self.padding_std_ratio * np.sqrt(self.batch_size) * std_edge)
self.max_node_num_global = max(self.max_node_num_global, max_node_single)
self.max_edge_num_global = max(self.max_edge_num_global, max_edge_single)
elif max_node is None:
if max_edge < max_edge_single:
raise ValueError(
f"the max_edge {max_edge} is less than the max length of a single sample {max_edge_single}")

if mean_edge != 0:
self.max_node_num_global = int(max_edge * mean_node / mean_edge)
else:
raise ValueError
self.max_node_num_global = max(self.max_node_num_global, max_node_single)
self.max_edge_num_global = max_edge
else:
if max_node < max_node_single:
raise ValueError(
f"the max_node {max_node} is less than the max length of a single sample {max_node_single}")

self.max_node_num_global = max_node
if mean_node != 0:
self.max_edge_num_global = int(max_node * mean_edge / mean_node)
else:
raise ValueError
self.max_edge_num_global = max(self.max_edge_num_global, max_edge_single)

def get_max_node_edge_num(self, node_attr, edge_attr, remainder=True):
"""get_max_node_edge_num

Args:
node_attr: node_attr
edge_attr: edge_attr
remainder (bool, optional): remainder. Defaults to True.

Returns:
max_node_num, max_edge_num
"""
max_node_num = 0
max_edge_num = 0
index = 0
for _ in range(self.step_num):
node_num = 0
edge_num = 0
for _ in range(self.batch_size):
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])
index += 1
max_node_num = max(max_node_num, node_num)
max_edge_num = max(max_edge_num, edge_num)

if remainder:
remain_num = self.sample_num - index - 1
node_num = 0
edge_num = 0
for _ in range(remain_num):
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])
index += 1
max_node_num = max(max_node_num, node_num)
max_edge_num = max(max_edge_num, edge_num)

return max_node_num, max_edge_num

def shuffle_index(self):
"""shuffle_index"""
indices = list(range(self.sample_num))
random.shuffle(indices)
return indices

### example of shuffling the input dataset, can be customized to specific dataset
def shuffle_action(self):
"""shuffle_action"""
indices = self.shuffle_index()
self.edge_index = [self.edge_index[i] for i in indices]
self.label = [self.label[i] for i in indices]
self.node_attr = [self.node_attr[i] for i in indices]
self.edge_attr = [self.edge_attr[i] for i in indices]

### example of generating the final shuffled dataset, can be customized to specific dataset
def shuffle(self):
"""shuffle"""
self.shuffle_action()
if not self.dynamic_batch_size:
max_node_num, max_edge_num = self.get_max_node_edge_num(self.node_attr, self.edge_attr, remainder=False)
while max_node_num > self.max_node_num_global or max_edge_num > self.max_edge_num_global:
self.shuffle_action()
max_node_num, max_edge_num = self.get_max_node_edge_num(self.node_attr, self.edge_attr, remainder=False)

self.step = 0
self.index = 0

def restart(self):
"""restart"""
self.step = 0
self.index = 0

### example of calculating dynamic batch size to avoid exceeding the max length of node and edge, can be customized to specific dataset
def get_batch_size(self, node_attr, edge_attr, start_batch_size):
"""get_batch_size

Args:
node_attr: node_attr
edge_attr: edge_attr
start_batch_size: start_batch_size

Returns:
batch_size
"""
node_num = 0
edge_num = 0
for i in range(start_batch_size):
index = self.index + i
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])

exceeding = False
while node_num > self.max_node_num_global or edge_num > self.max_edge_num_global:
node_num -= len(node_attr[index])
edge_num -= len(edge_attr[index])
index -= 1
exceeding = True
self.batch_exceeding_num += 1
if exceeding:
self.batch_change_num += 1

return index - self.index + 1

def gen_common_data(self, node_attr, edge_attr):
"""gen_common_data

Args:
node_attr: node_attr
edge_attr: edge_attr

Returns:
common_data
"""
if self.dynamic_batch_size:
if self.step >= self.step_num:
batch_size = self.get_batch_size(node_attr, edge_attr,
min((self.sample_num - self.index), self.batch_size))
else:
batch_size = self.get_batch_size(node_attr, edge_attr, self.batch_size)
else:
batch_size = self.batch_size

######################## node_batch
node_batch_step = []
sample_num = 0
for i in range(self.index, self.index + batch_size):
node_batch_step.extend([sample_num] * node_attr[i].shape[0])
sample_num += 1
node_batch_step = np.array(node_batch_step)
node_num = node_batch_step.shape[0]

######################## edge_index
edge_index_step = np.array([[], []], dtype=np.int64)
max_edge_index = 0
for i in range(self.index, self.index + batch_size):
edge_index_step = np.concatenate((edge_index_step, self.edge_index[i] + max_edge_index), 1)
max_edge_index = np.max(edge_index_step) + 1
edge_num = edge_index_step.shape[1]

######################### padding
edge_index_step = self.pad_zero_to_end(edge_index_step, 1, self.max_edge_num_global - edge_num)
node_batch_step = self.pad_zero_to_end(node_batch_step, 0, self.max_node_num_global - node_num)

######################### mask
node_mask = self.gen_mask(self.max_node_num_global, node_num)
edge_mask = self.gen_mask(self.max_edge_num_global, edge_num)
batch_size_mask = self.gen_mask(self.batch_size, batch_size)

######################### make Tensor
edge_index_step = Tensor(edge_index_step, ms.int32)
node_batch_step = Tensor(node_batch_step, ms.int32)
node_mask = Tensor(node_mask)
edge_mask = Tensor(edge_mask)
batch_size_mask = Tensor(batch_size_mask)

return CommonData(edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size).get_tuple_data()

def gen_node_attr(self, node_attr, batch_size, node_num):
"""gen_node_attr"""
node_attr_step = np.concatenate(node_attr[self.index:self.index + batch_size], 0)
node_attr_step = self.pad_zero_to_end(node_attr_step, 0, self.max_node_num_global - node_num)
node_attr_step = Tensor(node_attr_step)
return node_attr_step

def gen_edge_attr(self, edge_attr, batch_size, edge_num):
"""gen_edge_attr"""
edge_attr_step = np.concatenate(edge_attr[self.index:self.index + batch_size], 0)
edge_attr_step = self.pad_zero_to_end(edge_attr_step, 0, self.max_edge_num_global - edge_num)
edge_attr_step = Tensor(edge_attr_step)
return edge_attr_step

def gen_global_attr(self, global_attr, batch_size):
"""gen_global_attr"""
global_attr_step = np.stack(global_attr[self.index:self.index + batch_size], 0)
global_attr_step = self.pad_zero_to_end(global_attr_step, 0, self.batch_size - batch_size)
global_attr_step = Tensor(global_attr_step)
return global_attr_step

def add_step_index(self, batch_size):
"""add_step_index"""
self.index = self.index + batch_size
self.step += 1

class CommonData:
"""CommonData"""
def __init__(self, edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size):
self.tuple_data = (edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size)

def get_tuple_data(self):
"""get_tuple_data"""
return self.tuple_data

+ 0
- 278
MindChem/applications/nequip/mindchemistry/graph/normlization.py View File

@@ -1,278 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""norm"""
import mindspore as ms
from mindspore import ops, Parameter, nn
from .graph import AggregateNodeToGlobal, LiftGlobalToNode


class BatchNormMask(nn.Cell):
"""BatchNormMask"""

def __init__(self, num_features, eps=1e-5, momentum=0.9, affine=True):
super().__init__()
self.num_features = num_features
self.eps = eps
self.momentum = momentum
self.affine = affine
self.moving_mean = Parameter(ops.zeros((num_features,), ms.float32), name="moving_mean", requires_grad=False)
self.moving_variance = Parameter(ops.ones((num_features,), ms.float32),
name="moving_variance",
requires_grad=False)
if affine:
self.gamma = Parameter(ops.ones((num_features,), ms.float32), name="gamma", requires_grad=True)
self.beta = Parameter(ops.zeros((num_features,), ms.float32), name="beta", requires_grad=True)

def construct(self, x, mask, num):
"""construct"""
if x.shape[1] != self.num_features:
raise ValueError(f"x.shape[1] {x.shape[1]} is not equal to num_features {self.num_features}")
if x.shape[0] != mask.shape[0]:
raise ValueError(f"x.shape[0] {x.shape[0]} is not equal to mask.shape[0] {mask.shape[0]}")

if x.ndim != mask.ndim:
if mask.size != mask.shape[0]:
raise ValueError("mask.ndim dose not match src.ndim, and cannot be broadcasted to the same")
shape = [1] * x.ndim
shape[0] = -1
mask = ops.reshape(mask, shape).astype(x.dtype)
x = ops.mul(x, mask)

# pylint: disable=R1705
if x.ndim > 2:
norm_axis = []
shape = [-1]
for i in range(2, x.ndim):
norm_axis.append(i)
shape.append(1)

if self.training:
mean = ops.div(ops.sum(x, 0), num)
mean = ops.mean(mean, norm_axis)
self.moving_mean = self.momentum * self.moving_mean + (1 - self.momentum) * mean
mean = ops.reshape(mean, shape)
mean = ops.mul(mean, mask)
x = x - mean

var = ops.div(ops.sum(ops.pow(x, 2), 0), num)
var = ops.mean(var, norm_axis)
self.moving_variance = self.momentum * self.moving_variance + (1 - self.momentum) * var
std = ops.sqrt(ops.add(var, self.eps))
std = ops.reshape(std, shape)
y = ops.true_divide(x, std)
else:
mean = ops.reshape(self.moving_mean.astype(x.dtype), shape)
mean = ops.mul(mean, mask)
std = ops.sqrt(ops.add(self.moving_variance.astype(x.dtype), self.eps))
std = ops.reshape(std, shape)
y = ops.true_divide(ops.sub(x, mean), std)

if self.affine:
gamma = ops.reshape(self.gamma.astype(x.dtype), shape)
beta = ops.reshape(self.beta.astype(x.dtype), shape) * mask
y = y * gamma + beta

return y
else:
if self.training:
mean = ops.div(ops.sum(x, 0), num)
self.moving_mean = self.momentum * self.moving_mean + (1 - self.momentum) * mean
mean = ops.mul(mean, mask)
x = x - mean

var = ops.div(ops.sum(ops.pow(x, 2), 0), num)
self.moving_variance = self.momentum * self.moving_variance + (1 - self.momentum) * var
std = ops.sqrt(ops.add(var, self.eps))
y = ops.true_divide(x, std)
else:
mean = ops.mul(self.moving_mean.astype(x.dtype), mask)
std = ops.sqrt(ops.add(self.moving_variance.astype(x.dtype), self.eps))
y = ops.true_divide(ops.sub(x, mean), std)

if self.affine:
beta = self.beta.astype(x.dtype) * mask
y = y * self.gamma.astype(x.dtype) + beta

return y


class GraphLayerNormMask(nn.Cell):
"""GraphLayerNormMask"""

def __init__(self,
normalized_shape,
begin_norm_axis=-1,
eps=1e-5,
sub_mean=True,
divide_std=True,
affine_weight=True,
affine_bias=True,
aggr_mode="mean"):
super().__init__()
self.normalized_shape = normalized_shape
self.begin_norm_axis = begin_norm_axis
self.eps = eps
self.sub_mean = sub_mean
self.divide_std = divide_std
self.affine_weight = affine_weight
self.affine_bias = affine_bias
self.mean = ops.ReduceMean(keep_dims=True)
self.aggregate = AggregateNodeToGlobal(mode=aggr_mode)
self.lift = LiftGlobalToNode(mode="multi_graph")

if affine_weight:
self.gamma = Parameter(ops.ones(normalized_shape, ms.float32), name="gamma", requires_grad=True)
if affine_bias:
self.beta = Parameter(ops.zeros(normalized_shape, ms.float32), name="beta", requires_grad=True)

def construct(self, x, batch, mask, dim_size, scale=None):
"""construct"""
begin_norm_axis = self.begin_norm_axis if self.begin_norm_axis >= 0 else self.begin_norm_axis + x.ndim
if begin_norm_axis not in range(1, x.ndim):
raise ValueError(f"begin_norm_axis {begin_norm_axis} is not in range 1 to {x.ndim}")

norm_axis = []
for i in range(begin_norm_axis, x.ndim):
norm_axis.append(i)
if self.normalized_shape[i - begin_norm_axis] != x.shape[i]:
raise ValueError(f"x.shape[{i}] {x.shape[i]} is not equal to normalized_shape[{i - begin_norm_axis}] "
f"{self.normalized_shape[i - begin_norm_axis]}")

if x.shape[0] != mask.shape[0]:
raise ValueError(f"x.shape[0] {x.shape[0]} is not equal to mask.shape[0] {mask.shape[0]}")
if x.shape[0] != batch.shape[0]:
raise ValueError(f"x.shape[0] {x.shape[0]} is not equal to batch.shape[0] {batch.shape[0]}")

if x.ndim != mask.ndim:
if mask.size != mask.shape[0]:
raise ValueError("mask.ndim dose not match src.ndim, and cannot be broadcasted to the same")
shape = [1] * x.ndim
shape[0] = -1
mask = ops.reshape(mask, shape).astype(x.dtype)
x = ops.mul(x, mask)

if self.sub_mean:
mean = self.aggregate(x, batch, dim_size=dim_size, mask=mask)
mean = self.mean(mean, norm_axis)
mean = self.lift(mean, batch)
mean = ops.mul(mean, mask)
x = x - mean

if self.divide_std:
var = self.aggregate(ops.square(x), batch, dim_size=dim_size, mask=mask)
var = self.mean(var, norm_axis)
if scale is not None:
var = var * scale
std = ops.sqrt(var + self.eps)
std = self.lift(std, batch)
x = ops.true_divide(x, std)

if self.affine_weight:
x = x * self.gamma.astype(x.dtype)

if self.affine_bias:
beta = ops.mul(self.beta.astype(x.dtype), mask)
x = x + beta

return x


class GraphInstanceNormMask(nn.Cell):
"""GraphInstanceNormMask"""

def __init__(self,
num_features,
eps=1e-5,
sub_mean=True,
divide_std=True,
affine_weight=True,
affine_bias=True,
aggr_mode="mean"):
super().__init__()
self.num_features = num_features
self.eps = eps
self.sub_mean = sub_mean
self.divide_std = divide_std
self.affine_weight = affine_weight
self.affine_bias = affine_bias
self.mean = ops.ReduceMean(keep_dims=True)
self.aggregate = AggregateNodeToGlobal(mode=aggr_mode)
self.lift = LiftGlobalToNode(mode="multi_graph")

if affine_weight:
self.gamma = Parameter(ops.ones((self.num_features,), ms.float32), name="gamma", requires_grad=True)
if affine_bias:
self.beta = Parameter(ops.zeros((self.num_features,), ms.float32), name="beta", requires_grad=True)

def construct(self, x, batch, mask, dim_size, scale=None):
"""construct"""
if x.shape[1] != self.num_features:
raise ValueError(f"x.shape[1] {x.shape[1]} is not equal to num_features {self.num_features}")
if x.shape[0] != mask.shape[0]:
raise ValueError(f"x.shape[0] {x.shape[0]} is not equal to mask.shape[0] {mask.shape[0]}")
if x.shape[0] != batch.shape[0]:
raise ValueError(f"x.shape[0] {x.shape[0]} is not equal to batch.shape[0] {batch.shape[0]}")

if x.ndim != mask.ndim:
if mask.size != mask.shape[0]:
raise ValueError("mask.ndim dose not match src.ndim, and cannot be broadcasted to the same")
shape = [1] * x.ndim
shape[0] = -1
mask = ops.reshape(mask, shape).astype(x.dtype)
x = ops.mul(x, mask)
gamma = None # 后来添加,防止未定义报错
if x.ndim > 2:
norm_axis = []
shape = [-1]
for i in range(2, x.ndim):
norm_axis.append(i)
shape.append(1)

if self.affine_weight:
gamma = ops.reshape(self.gamma.astype(x.dtype), shape)
if self.affine_bias:
beta = ops.reshape(self.beta.astype(x.dtype), shape)
else:
if self.affine_weight:
gamma = self.gamma.astype(x.dtype)
if self.affine_bias:
beta = self.beta.astype(x.dtype)

if self.sub_mean:
mean = self.aggregate(x, batch, dim_size=dim_size, mask=mask)
if x.ndim > 2:
mean = self.mean(mean, norm_axis)
mean = self.lift(mean, batch)
mean = ops.mul(mean, mask)
x = x - mean

if self.divide_std:
var = self.aggregate(ops.square(x), batch, dim_size=dim_size, mask=mask)
if x.ndim > 2:
var = self.mean(var, norm_axis)
if scale is not None:
var = var * scale
std = ops.sqrt(var + self.eps)
std = self.lift(std, batch)
x = ops.true_divide(x, std)

if self.affine_weight:
x = x * gamma

if self.affine_bias:
beta = ops.mul(beta, mask)
x = x + beta

return x

+ 0
- 19
MindChem/applications/nequip/mindchemistry/so2_conv/__init__.py View File

@@ -1,19 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
init file
"""
from .so3 import SO3Rotation
from .so2 import SO2Convolution

+ 0
- 64
MindChem/applications/nequip/mindchemistry/so2_conv/init_edge_rot_mat.py View File

@@ -1,64 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
file to get rotating matrix from edge distance vector
"""
from mindspore import ops
import mindspore.numpy as ms_np


def init_edge_rot_mat(edge_distance_vec):
"""
get rotating matrix from edge distance vector
"""
epsilon = 0.00000001
edge_vec_0 = edge_distance_vec
edge_vec_0_distance = ops.sqrt(ops.maximum(ops.sum(edge_vec_0 ** 2, dim=1), epsilon))
# Make sure the atoms are far enough apart
norm_x = ops.div(edge_vec_0, edge_vec_0_distance.view(-1, 1))
edge_vec_2 = ops.rand_like(edge_vec_0) - 0.5

edge_vec_2 = ops.div(edge_vec_2, ops.sqrt(ops.maximum(ops.sum(edge_vec_2 ** 2, dim=1), epsilon)).view(-1, 1))
# Create two rotated copies of the random vectors in case the random vector is aligned with norm_x
# With two 90 degree rotated vectors, at least one should not be aligned with norm_x
edge_vec_2b = edge_vec_2.copy()
edge_vec_2b[:, 0] = -edge_vec_2[:, 1]
edge_vec_2b[:, 1] = edge_vec_2[:, 0]
edge_vec_2c = edge_vec_2.copy()
edge_vec_2c[:, 1] = -edge_vec_2[:, 2]
edge_vec_2c[:, 2] = edge_vec_2[:, 1]
vec_dot_b = ops.abs(ops.sum(edge_vec_2b * norm_x, dim=1)).view(-1, 1)
vec_dot_c = ops.abs(ops.sum(edge_vec_2c * norm_x, dim=1)).view(-1, 1)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1)).view(-1, 1)
edge_vec_2 = ops.where(ops.broadcast_to(ops.gt(vec_dot, vec_dot_b), edge_vec_2b.shape), edge_vec_2b, edge_vec_2)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1)).view(-1, 1)
edge_vec_2 = ops.where(ops.broadcast_to(ops.gt(vec_dot, vec_dot_c), edge_vec_2c.shape), edge_vec_2c, edge_vec_2)
vec_dot = ops.abs(ops.sum(edge_vec_2 * norm_x, dim=1))
# Check the vectors aren't aligned

norm_z = ms_np.cross(norm_x, edge_vec_2, axis=1)
norm_z = ops.div(norm_z, ops.sqrt(ops.maximum(ops.sum(norm_z ** 2, dim=1, keepdim=True), epsilon)))
norm_z = ops.div(norm_z, ops.sqrt(ops.maximum(ops.sum(norm_z ** 2, dim=1), epsilon)).view(-1, 1))

norm_y = ms_np.cross(norm_x, norm_z, axis=1)
norm_y = ops.div(norm_y, ops.sqrt(ops.maximum(ops.sum(norm_y ** 2, dim=1, keepdim=True), epsilon)))
# Construct the 3D rotation matrix
norm_x = norm_x.view(-1, 3, 1)
norm_y = -norm_y.view(-1, 3, 1)
norm_z = norm_z.view(-1, 3, 1)
edge_rot_mat_inv = ops.cat([norm_z, norm_x, norm_y], axis=2)

edge_rot_mat = ops.swapaxes(edge_rot_mat_inv, 1, 2)
return edge_rot_mat

BIN
MindChem/applications/nequip/mindchemistry/so2_conv/jd.pkl View File


+ 0
- 260
MindChem/applications/nequip/mindchemistry/so2_conv/so2.py View File

@@ -1,260 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
so2 file
"""
import mindspore as ms
from mindspore import ops, nn
from mindscience.e3nn.o3 import Irreps


class Silu(nn.Cell):
"""
silu activation class
"""

def __init__(self):
super().__init__()
self.sigmoid = nn.Sigmoid()

def construct(self, x):
"""
silu activation class construct process
"""
return ops.mul(x, self.sigmoid(x))


class SO2MConvolution(nn.Cell):
"""
SO2 Convolution subnetwork
"""

def __init__(self, in_channels, out_channels):
super().__init__()
self.fc = nn.Dense(in_channels // 2, out_channels,
has_bias=False).to_float(ms.float16)
self.out_channels = out_channels

def construct(self, x_m):
"""
SO2 Convolution sub network construct process
"""
x_m = self.fc(x_m).astype(ms.float32)
x_i = ops.narrow(x_m, 2, 0, self.out_channels // 2)
x_r = ops.narrow(x_m, 2, self.out_channels // 2,
self.out_channels // 2)

x_m_r = ops.narrow(x_r, 1, 1, 1) - ops.narrow(
x_i, 1, 0, 1) # x_r[:, 1] - x_i[:, 0]
x_m_i = ops.narrow(x_i, 1, 1, 1) + ops.narrow(
x_r, 1, 0, 1) # x_i[:, 1] + x_r[:, 0]

x_out = ops.cat((x_m_i, x_m_r), axis=1)
return x_out


class SO2Convolution(nn.Cell):
"""
SO2 Convolution network
"""

def __init__(self, irreps_in, irreps_out):
super().__init__()

self.irreps_in1 = Irreps(irreps_in)
self.irreps_out = Irreps(irreps_out)

self.max_order_in = -1
for mulir in self.irreps_in1:
self.max_order_in = max(self.max_order_in, mulir.ir.l)
self.max_order_out = -1
for mulir in self.irreps_out:
self.max_order_out = max(self.max_order_out, mulir.ir.l)

self.m_shape_dict_in, self.irreps_in1_length = self.get_m_info(
self.irreps_in1, self.max_order_in)
self.m_shape_dict_out, self.irreps_out_length = self.get_m_info(
self.irreps_out, self.max_order_out)

self.fc_m0 = nn.Dense(self.m_shape_dict_in.get(0, None),
self.m_shape_dict_out.get(0, None)).to_float(ms.float16)

self.global_max_order = min(self.max_order_in + 1,
self.max_order_out + 1)

self.so2_m_conv = nn.CellList([])
for i in range(self.global_max_order):
if i == 0:
continue
so2_m_convolution = SO2MConvolution(self.m_shape_dict_in.get(i, None),
self.m_shape_dict_out.get(i, None))
self.so2_m_conv.append(so2_m_convolution)

self.max_m_in = 2 * self.max_order_in + 1

self.irreps_out_data = []
for mulir in self.irreps_out:
key = mulir.ir.l
value = mulir.mul
self.irreps_out_data.append((key, value))

def get_m_info(self, irreps, max_order):
"""
helper function to get m_info
"""
m_shape_dict = {}
m_mul_ir_l_dict = {}
for i in range(max_order + 1):
m_shape_dict[i] = 0

for mulir in irreps:
mul = mulir.mul
ir_l = mulir.ir.l
if ir_l not in m_mul_ir_l_dict:
m_mul_ir_l_dict[ir_l] = mul
else:
m_mul_ir_l_dict[ir_l] = m_mul_ir_l_dict[ir_l] + mul
for j in range(mulir.ir.l + 1):
if j == 0:
m_shape_dict[j] = m_shape_dict[j] + mul
else:
m_shape_dict[j] = m_shape_dict[j] + 2 * mul

return m_shape_dict, len(irreps)

def get_m_list_merge(self, x):
"""
helper function to get m_list_merge
"""
m_list = []
for _ in range(self.max_m_in):
m_list.append([])

index_shifting = int((self.max_m_in - 1) / 2)
for tmp in x:
m_length = tmp.shape[-1]
m_shift = int((m_length - 1) / 2)
for j in range(m_length):
m_list[j - m_shift + index_shifting].append(tmp[:, :, j])

m_list_merge = []
for i in range(index_shifting + 1):
if i == 0:
m_list_merge.append(ops.cat(m_list[index_shifting - i], -1))
else:
m_list_merge.append(
ops.cat((ops.cat(m_list[index_shifting - i], -1),
ops.cat(m_list[index_shifting + i], -1)), -1))
return m_list_merge

def construct(self, x, x_edge):
"""
SO2 Convolution network construct process
"""
##################### _m_primary #########################
num_edges = ops.shape(x_edge)[0]
m_list_merge = self.get_m_list_merge(x)
# ##################### finish _m_primary #########################
# radial function
out = []

### Compute m=0 coefficients separately since they only have real values
x_0 = m_list_merge[0]

x_0 = self.fc_m0(x_0).astype(ms.float32)
out.append(x_0)

#### Compute the values for the m > 0 coefficients
for m in range(self.global_max_order):
if m == 0:
continue
x_m = m_list_merge[m]
x_m = x_m.reshape(num_edges, 2, -1)
x_m = self.so2_m_conv[m - 1](x_m)
out.append(x_m)

###################### start fill 0 ######################
if self.max_order_out + 1 > len(m_list_merge):
for m in range(len(m_list_merge), self.max_order_out + 1):
extra_zero = ops.zeros(
(num_edges, 2, int(self.m_shape_dict_out.get(m, None) / 2)))
out.append(extra_zero)
###################### finish fill 0 ######################

###################### start _l_primary #########################
l_primary_list_0 = []
l_primary_list_left = []
l_primary_list_right = []

for _ in range(self.irreps_out_length):
l_primary_list_0.append([])
l_primary_list_left.append([])
l_primary_list_right.append([])

m_0 = out[0]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= 0:
l_primary_list_0[index].append(
ops.unsqueeze(m_0[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

for m in range(1, len(out)):
right = out[m][:, 1]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= m:
l_primary_list_right[index].append(
ops.unsqueeze(right[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

for m in range(len(out) - 1, 0, -1):
left = out[m][:, 0]
offset = 0
index = 0

for key_val in self.irreps_out_data:
key = key_val[0]
value = key_val[1]
if key >= m:
l_primary_list_left[index].append(
ops.unsqueeze(left[:, offset:offset + value], -1))
offset = offset + value
index = index + 1

l_primary_list = []
for i in range(self.irreps_out_length):
if i == 0:
tmp = ops.cat(l_primary_list_0[i], -1)
l_primary_list.append(tmp)
else:
tmp = ops.cat(
(ops.cat((ops.cat(l_primary_list_left[i],
-1), ops.cat(l_primary_list_0[i], -1)),
-1), ops.cat(l_primary_list_right[i], -1)), -1)
l_primary_list.append(tmp)

##################### finish _l_primary #########################
return tuple(l_primary_list)

+ 0
- 156
MindChem/applications/nequip/mindchemistry/so2_conv/so3.py View File

@@ -1,156 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
so3 file
"""
import mindspore as ms
from mindspore import nn, ops, vmap, jit_class
from mindspore.numpy import tensordot
from mindscience.e3nn import o3
from mindscience.e3nn.o3 import Irreps

from .wigner import wigner_D


class SO3Embedding(nn.Cell):
"""
SO3Embedding class
"""

def __init__(self):
self.embedding = None

def _rotate(self, so3rotation, lmax_list, max_list):
"""
SO3Embedding rotate
"""
embedding_rotate = so3rotation[0].rotate(self.embedding, lmax_list[0],
max_list[0])
self.embedding = embedding_rotate

def _rotate_inv(self, so3rotation):
"""
SO3Embedding rotate inverse
"""
embedding_rotate = so3rotation[0].rotate_inv(self.embedding,
self.lmax_list[0],
self.mmax_list[0])
self.embedding = embedding_rotate


@jit_class
class SO3Rotation:
"""
SO3_Rotation class
"""

def __init__(self, lmax, irreps_in, irreps_out):
self.lmax = lmax
self.irreps_in1 = Irreps(irreps_in)
self.irreps_out = Irreps(irreps_out)
self.tensordot_vmap = vmap(tensordot, (0, 0, None), 0)

@staticmethod
def narrow(inputs, axis, start, length):
"""
SO3_Rotation narrow class
"""
begins = [0] * inputs.ndim
begins[axis] = start

sizes = list(inputs.shape)

sizes[axis] = length
res = ops.slice(inputs, begins, sizes)
return res

@staticmethod
def rotation_to_wigner_d_matrix(edge_rot_mat, start_lmax, end_lmax):
"""
SO3_Rotation rotation_to_wigner_d_matrix
"""
x = edge_rot_mat @ ms.Tensor([0.0, 1.0, 0.0])
alpha, beta = o3.xyz_to_angles(x)
rvalue = (ops.swapaxes(
o3.angles_to_matrix(alpha, beta, ops.zeros_like(alpha)), -1, -2)
@ edge_rot_mat)
gamma = ops.atan2(rvalue[..., 0, 2], rvalue[..., 0, 0])

block_list = []
for lmax in range(start_lmax, end_lmax + 1):
block = wigner_D(lmax, alpha, beta, gamma).astype(ms.float32)
block_list.append(block)
return block_list

def set_wigner(self, rot_mat3x3):
"""
SO3_Rotation set_wigner
"""
wigner = self.rotation_to_wigner_d_matrix(rot_mat3x3, 0, self.lmax)
wigner_inv = []
length = len(wigner)
for i in range(length):
wigner_inv.append(ops.swapaxes(wigner[i], 1, 2))
return tuple(wigner), tuple(wigner_inv)

def rotate(self, embedding, wigner):
"""
SO3_Rotation rotate
"""
res = []
batch_shape = embedding.shape[:-1]
for (s, l), mir in zip(self.irreps_in1.slice_tuples,
self.irreps_in1.data):
v_slice = self.narrow(embedding, -1, s, l)
if embedding.ndim == 1:
res.append((v_slice.reshape((1,) + batch_shape +
(mir.mul, mir.ir.dim)), mir.ir))
else:
res.append(
(v_slice.reshape(batch_shape + (mir.mul, mir.ir.dim)),
mir.ir))
rotate_data_list = []
for data, ir in res:
self.tensordot_vmap(data.astype(ms.float16),
wigner[ir.l].astype(ms.float16), ([1], [1]))
rotate_data = self.tensordot_vmap(data.astype(ms.float16),
wigner[ir.l].astype(ms.float16),
((1), (1))).astype(ms.float32)
rotate_data_list.append(rotate_data)
return tuple(rotate_data_list)

def rotate_inv(self, embedding, wigner_inv):
"""
SO3_Rotation rotate_inv
"""
res = []
batch_shape = embedding[0].shape[0:1]
index = 0
for (_, _), mir in zip(self.irreps_out.slice_tuples,
self.irreps_out.data):
v_slice = embedding[index]
if embedding[0].ndim == 1:
res.append((v_slice, mir.ir))
else:
res.append((v_slice, mir.ir))
index = index + 1
rotate_back_data_list = []
for data, ir in res:
rotate_back_data = self.tensordot_vmap(
data.astype(ms.float16), wigner_inv[ir.l].astype(ms.float16),
((1), (1))).astype(ms.float32)
rotate_back_data_list.append(
rotate_back_data.view(batch_shape + (-1,)))
return ops.cat(rotate_back_data_list, -1)

+ 0
- 61
MindChem/applications/nequip/mindchemistry/so2_conv/wigner.py View File

@@ -1,61 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
wigner file
"""

# pylint: disable=C0103
import pickle
from mindspore import ops
import mindspore as ms
from mindscience.e3nn.utils.func import broadcast_args


def wigner_D(lv, alpha, beta, gamma):
"""
# Borrowed from e3nn @ 0.4.0:
# https://github.com/e3nn/e3nn/blob/0.4.0/e3nn/o3/_wigner.py#L10
# jd is a list of tensors of shape (2l+1, 2l+1)

# Borrowed from e3nn @ 0.4.0:
# https://github.com/e3nn/e3nn/blob/0.4.0/e3nn/o3/_wigner.py#L37
#
# In 0.5.0, e3nn shifted to torch.matrix_exp which is significantly slower:
# https://github.com/e3nn/e3nn/blob/0.5.0/e3nn/o3/_wigner.py#L92
"""
jd = None
with open("jd.pkl", "rb") as f:
jd = pickle.load(f)
if not lv < len(jd):
raise NotImplementedError(
f"wigner D maximum l implemented is {len(jd) - 1}, send us an email to ask for more"
)
alpha, beta, gamma = broadcast_args(alpha, beta, gamma)
j = jd[lv]
xa = _z_rot_mat(alpha, lv)
xb = _z_rot_mat(beta, lv)
xc = _z_rot_mat(gamma, lv)
return xa @ j.astype(ms.float16) @ xb @ j.astype(ms.float16) @ xc


def _z_rot_mat(angle, lv):
shape = angle.shape
m = ops.zeros((shape[0], 2 * lv + 1, 2 * lv + 1))
inds = ops.arange(0, 2 * lv + 1, 1)
reversed_inds = ops.arange(2 * lv, -1, -1)
frequencies = ops.arange(lv, -lv - 1, -1)
m[..., inds, reversed_inds] = ops.sin(frequencies * angle[..., None])
m[..., inds, inds] = ops.cos(frequencies * angle[..., None])
return m.astype(ms.float16)

+ 0
- 18
MindChem/applications/nequip/mindchemistry/utils/__init__.py View File

@@ -1,18 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this filepio[] except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""init"""
from .load_config import load_yaml_config

__all__ = ['load_yaml_config']

+ 0
- 128
MindChem/applications/nequip/mindchemistry/utils/check_func.py View File

@@ -1,128 +0,0 @@
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""functions"""
from __future__ import absolute_import

from mindspore import context

_SPACE = " "


def _convert_to_tuple(params):
if params is None:
return params
if not isinstance(params, (list, tuple)):
params = (params,)
if isinstance(params, list):
params_out = tuple(params)
else:
params_out = params # ✅ 防止未定义
return params_out


def check_param_type(param, param_name, data_type=None, exclude_type=None):
"""Check parameter's data type"""
data_type = _convert_to_tuple(data_type)
exclude_type = _convert_to_tuple(exclude_type)

if data_type and not isinstance(param, data_type):
raise TypeError(
f"The type of {param_name} should be instance of {data_type}, but got {param} with type {type(param)}"
)
if exclude_type and type(param) in exclude_type:
raise TypeError(
f"The type of {param_name} should not be instance of {exclude_type},but got {param} with type {type(param)}"
)


def check_param_value(param, param_name, valid_value):
"""check parameter's value"""
valid_value = _convert_to_tuple(valid_value)
if param not in valid_value:
raise ValueError(f"The value of {param_name} should be in {valid_value}, but got {param}")


def check_param_type_value(param, param_name, valid_value, data_type=None, exclude_type=None):
"""check both data type and value"""
check_param_type(param, param_name, data_type=data_type, exclude_type=exclude_type)
check_param_value(param, param_name, valid_value)


def check_dict_type(param_dict, param_name, key_type=None, value_type=None):
"""check data type for key and value of the specified dict"""
check_param_type(param_dict, param_name, data_type=dict)

for key in param_dict.keys():
if key_type:
check_param_type(key, _SPACE.join(("key of", param_name)), data_type=key_type)
if value_type:
values = _convert_to_tuple(param_dict[key])
for value in values:
check_param_type(value, _SPACE.join(("value of", param_name)), data_type=value_type)


def check_dict_value(param_dict, param_name, key_value=None, value_value=None):
"""check values for key and value of specified dict"""
check_param_type(param_dict, param_name, data_type=dict)

for key in param_dict.keys():
if key_value:
check_param_value(key, _SPACE.join(("key of", param_name)), key_value)
if value_value:
values = _convert_to_tuple(param_dict[key])
for value in values:
check_param_value(value, _SPACE.join(("value of", param_name)), value_value)


def check_dict_type_value(param_dict, param_name, key_type=None, value_type=None, key_value=None, value_value=None):
"""check values for key and value of specified dict"""
check_dict_type(param_dict, param_name, key_type=key_type, value_type=value_type)
check_dict_value(param_dict, param_name, key_value=key_value, value_value=value_value)


def check_mode(api_name):
"""check running mode"""
if context.get_context("mode") == context.PYNATIVE_MODE:
raise RuntimeError(f"{api_name} is only supported GRAPH_MODE now but got PYNATIVE_MODE")


def check_param_no_greater(param, param_name, compared_value):
""" Check whether the param less than the given compared_value"""
if param > compared_value:
raise ValueError(f"The value of {param_name} should be no greater than {compared_value}, but got {param}")


def check_param_odd(param, param_name):
""" Check whether the param is an odd number"""
if param % 2 == 0:
raise ValueError(f"The value of {param_name} should be an odd number, but got {param}")


def check_param_even(param, param_name):
""" Check whether the param is an even number"""
for value in param:
if value % 2 != 0:
raise ValueError(f"The value of {param_name} should be an even number, but got {param}")


def check_lr_param_type_value(param, param_name, param_type, thresh_hold=0, restrict=False, exclude=None):
if (exclude and isinstance(param, exclude)) or not isinstance(param, param_type):
raise TypeError(f"the type of {param_name} should be {param_type}, but got {type(param)}")
if restrict:
if param <= thresh_hold:
raise ValueError(f"the value of {param_name} should be > {thresh_hold}, but got: {param}")
else:
if param < thresh_hold:
raise ValueError(f"the value of {param_name} should be >= {thresh_hold}, but got: {param}")

+ 6
- 0
MindChem/applications/nequip/models/__init__.py View File

@@ -0,0 +1,6 @@
# models/__init__.py
"""Nequip models package.

Modules are imported explicitly, e.g.:
from models.nequip import Nequip
"""

MindChem/applications/matformer/mindchemistry/cell/convolution.py → MindChem/applications/nequip/models/convolution.py View File

@@ -16,7 +16,7 @@
from mindspore import nn, ops, float32
from mindscience.e3nn.o3 import TensorProduct, Irreps, Linear
from mindscience.e3nn.nn import FullyConnectedNet
from ..graph.graph import AggregateEdgeToNode
from .graph import AggregateEdgeToNode

softplus = ops.Softplus()


MindChem/applications/matformer/mindchemistry/cell/embedding.py → MindChem/applications/nequip/models/embedding.py View File


MindChem/applications/nequip/mindchemistry/graph/graph.py → MindChem/applications/nequip/models/graph.py View File


MindChem/applications/nequip/mindchemistry/utils/load_config.py → MindChem/applications/nequip/models/load_config.py View File

@@ -53,7 +53,7 @@ def load_yaml_config(file_path):
``Ascend`` ``CPU`` ``GPU``

Examples:
>>> from mindchemistry.utils import load_yaml_config
>>> from models.utils import load_yaml_config
>>> config_file_path = 'xxx' # 'xxx' is the file_path
>>> configs = load_yaml_config(config_file_path)
"""

MindChem/applications/nequip/mindchemistry/cell/message_passing.py → MindChem/applications/nequip/models/message_passing.py View File


MindChem/applications/nequip/mindchemistry/cell/nequip.py → MindChem/applications/nequip/models/nequip.py View File

@@ -17,7 +17,7 @@ from mindspore import nn, float32, int32
from mindscience.e3nn.o3 import Irreps, SphericalHarmonics, Linear
from mindscience.e3nn.nn import OneHot
from mindscience.e3nn.utils import radius_graph
from ..graph.graph import AggregateNodeToGlobal
from .graph import AggregateNodeToGlobal
from .message_passing import MessagePassing
from .embedding import RadialEdgeEmbedding


MindChem/applications/nequip/mindchemistry/cell/matformer/utils.py → MindChem/applications/nequip/models/utils.py View File


+ 59
- 81
MindChem/applications/nequip/nequip.ipynb
File diff suppressed because it is too large
View File


+ 2
- 2
MindChem/applications/nequip/nequip_en.ipynb View File

@@ -185,7 +185,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"id": "c9ba202b",
"metadata": {},
"outputs": [],
@@ -196,7 +196,7 @@
"import mindspore as ms\n",
"from mindspore import nn, Profiler\n",
"from src.dataset import _unpack, create_training_dataset\n",
"from mindchemistry.cell import Nequip\n",
"from models.nequip import Nequip\n",
"from tqdm.notebook import tqdm\n",
"\n",
"\n",


+ 1
- 1
MindChem/applications/nequip/predict.py View File

@@ -21,7 +21,7 @@ import argparse
import numpy as np
import mindspore as ms

from mindchemistry.utils.load_config import load_yaml_config_from_path
from models.load_config import load_yaml_config_from_path
from src import predicter
from src.plot import print_configuration
from src.utils import log_config


+ 1
- 1
MindChem/applications/nequip/rmd.yaml View File

@@ -17,7 +17,7 @@ model:
hidden_mul: 64

optimizer:
num_epoch: 2 # 为快速验证功能,设置较小的epoch数,具体使用时建议调整至800以上
num_epoch: 850 # It is recommended to adjust it to above 800 when in use
eval_steps: 10
warmup_steps: 6400
learning_rate: 0.01 # learning rate


+ 1
- 1
MindChem/applications/nequip/src/predicter.py View File

@@ -23,7 +23,7 @@ import mindspore as ms
from mindspore import nn
from src.dataset import create_training_dataset, _unpack

from mindchemistry.cell import Nequip
from models.nequip import Nequip


def evaluation(dtype, configs):


+ 1
- 1
MindChem/applications/nequip/src/trainer.py View File

@@ -26,7 +26,7 @@ from mindspore import nn
from src.dataset import create_training_dataset, _unpack
from src.utils import training_bar
from mindchemistry.cell import Nequip
from models.nequip import Nequip
def generate_learning_rate(learning_rate, warmup_steps, step_num):


+ 1
- 1
MindChem/applications/nequip/train.py View File

@@ -21,7 +21,7 @@ import argparse
import numpy as np
import mindspore as ms

from mindchemistry.utils.load_config import load_yaml_config_from_path
from models.load_config import load_yaml_config_from_path
from src import trainer
from src.plot import plot_loss, plot_lr, print_configuration
from src.utils import log_config


+ 38
- 26
MindChem/applications/orb/README.md View File

@@ -10,7 +10,7 @@

## Environment Requirements

> 1. Install `mindspore (2.5.0)`
> 1. Install `mindspore (2.7.0)`
> 2. Install dependencies: `pip install -r requirement.txt`

## Quick Start
@@ -28,32 +28,44 @@
```text
The main code modules are in the src folder, with the dataset folder containing the datasets, the orb_ckpts folder containing pre-trained models and trained model weight files, and the configs folder containing parameter configuration files for each code.

orb_models # Model name
orb_models # ORB pre-training / fine-tuning project
├── dataset
├── train_mptrj_ase.db # Training dataset for fine-tuning stage
└── val_mptrj_ase.db # Test dataset for fine-tuning stage
├── orb_ckpts
└── orb-mptraj-only-v2.ckpt # Pre-trained model checkpoint
├── configs
├── config.yaml # Single-card training parameter configuration file
├── config_parallel.yaml # Multi-card parallel training parameter configuration file
└── config_eval.yaml # Inference parameter configuration file
├── src
├── __init__.py
├── ase_dataset.py # Process and load datasets
├── atomic_system.py # Define data structure for atomic systems
├── base.py # Base class definitions
├── featurization_utilities.py # Provide tools to convert atomic systems into feature vectors
├── pretrained.py # Pre-trained model related functions
├── property_definitions.py # Define calculation methods and naming rules for various physical properties in atomic systems
├── trainer.py # Model loss class definitions
├── segment_ops.py # Provide tools for segmenting data
└── utils.py # Utility module
├── finetune.py # Model fine-tuning code
├── evaluate.py # Model inference code
├── run.sh # Single-card training startup script
├── run_parallel.sh # Multi-card parallel training startup script
└── requirement.txt # Environment
│ ├── train_mptrj_ase.db # Training dataset for fine-tuning (ASE trajectories, SQLite)
│ └── val_mptrj_ase.db # Validation / test dataset for fine-tuning
├── orb_ckpts # Directory for pre-trained & fine-tuned checkpoints
│ └── orb-mptraj-only-v2.ckpt # Pre-trained ORB checkpoint (mptraj-only task)
├── configs # Config files for training / inference
│ ├── config.yaml # Single-card training configuration (lr, batch_size, etc.)
│ ├── config_parallel.yaml # Multi-card data-parallel training configuration
│ └── config_eval.yaml # Inference / evaluation configuration
├── src # Core code for data processing and training
│ ├── __init__.py # Package initializer for src
│ ├── ase_dataset.py # Load and wrap ASE datasets (read SQLite, build atomic graphs)
│ ├── atomic_system.py # Data structures for atomic systems (positions, species, cell, etc.)
│ ├── base.py # Common base classes and utilities (e.g., batch_graphs)
│ ├── featurization_utilities.py # Tools to convert atomic systems into model input features
│ ├── pretrained.py # Interfaces for building and loading pre-trained ORB models
│ ├── property_definitions.py # Config and naming rules for energy / forces / stress, etc.
│ ├── trainer.py # Training loop and loss wrappers (e.g., OrbLoss)
│ ├── segment_ops.py # Segment-wise reduction ops (segment_sum / mean / max)
│ └── utils.py # Utility functions (seeding, logging, optimizer & LR scheduler)
├── models # Model definitions (GNN / ORB networks)
│ ├── __init__.py # Package initializer for orb
│ ├── gns.py # GNS (Graph Network Simulator) related structures / APIs
│ ├── orb.py # Main ORB architecture (encoder + heads)
│ └── utils.py # Internal utilities and helper modules for ORB
├── finetune.py # Entry script for model fine-tuning
├── evaluate.py # Entry script for model inference / evaluation
├── run.sh # Single-card training launcher (wraps finetune.py + config.yaml)
├── run_parallel.sh # Multi-card training launcher (msrun + config_parallel.yaml)
└── requirement.txt # Python dependency list for environment setup

```

## Download Dataset


+ 38
- 26
MindChem/applications/orb/README_CN.md View File

@@ -10,7 +10,7 @@

## 环境要求

> 1. 安装`mindspore(2.5.0)`
> 1. 安装`mindspore(2.7.0)`
> 2. 安装依赖包:`pip install -r requirement.txt`

## 快速入门
@@ -28,32 +28,44 @@
```text
代码主要模块在src文件夹下,其中dataset文件夹下是数据集,orb_ckpts文件夹下是预训练模型和训练好的模型权重文件,configs文件夹下是各代码的参数配置文件。

orb_models # 模型名
orb_models # ORB 预训练 / 微调工程
├── dataset
├── train_mptrj_ase.db # 微调阶段训练数据集
└── val_mptrj_ase.db # 微调阶段测试数据集
├── orb_ckpts
└── orb-mptraj-only-v2.ckpt # 预训练模型checkpoint
├── configs
├── config.yaml # 单卡训练参数配置文件
├── config_parallel.yaml # 多卡并行训练参数配置文件
└── config_eval.yaml # 推理参数配置文件
├── src
├── __init__.py
├── ase_dataset.py # 处理和加载数据集
├── atomic_system.py # 定义原子系统的数据结构
├── base.py # 基础类定义
├── featurization_utilities.py # 提供将原子系统转换为特征向量的工具
├── pretrained.py # 预训练模型相关函数
├── property_definitions.py # 定义原子系统中各种物理性质的计算方式和命名规则
├── trainer.py # 模型loss类定义
├── segment_ops.py # 提供对数据进行分段处理的工具
└── utils.py # 工具模块
├── finetune.py # 模型微调代码
├── evaluate.py # 模型推理代码
├── run.sh # 单卡训练启动脚本
├── run_parallel.sh # 多卡并行训练启动脚本
└── requirement.txt # 环境
│ ├── train_mptrj_ase.db # 微调训练集(ASE 轨迹,SQLite 格式)
│ └── val_mptrj_ase.db # 微调验证 / 测试集
├── orb_ckpts # 预训练 & 微调模型 ckpt 存放目录
│ └── orb-mptraj-only-v2.ckpt # 仅 mptraj 任务的预训练 ORB 模型
├── configs # 训练 / 推理配置
│ ├── config.yaml # 单卡训练配置(学习率、batch_size 等)
│ ├── config_parallel.yaml # 多卡数据并行训练配置
│ └── config_eval.yaml # 推理 / 评估配置
├── src # 数据处理与训练核心源码
│ ├── __init__.py # src 包初始化
│ ├── ase_dataset.py # ASE 数据集读取与封装(读 SQLite、组装原子图)
│ ├── atomic_system.py # 原子系统数据结构定义(坐标、原子种类、晶胞信息等)
│ ├── base.py # 通用基类与工具(batch_graphs 等图数据打包)
│ ├── featurization_utilities.py # 原子系统 → 模型输入特征张量的特征化工具
│ ├── pretrained.py # 预训练 ORB 模型构造与加载接口
│ ├── property_definitions.py # 能量 / 力 / 应力等物理量配置与命名
│ ├── trainer.py # 训练循环与 OrbLoss 等损失封装
│ ├── segment_ops.py # segment_sum / mean / max 等分段归约算子
│ └── utils.py # 通用工具函数(随机种子、日志、优化器、LR scheduler 等)
├── models # 模型结构定义(GNN / ORB 等)
│ ├── __init__.py # orb 子包初始化
│ ├── gns.py # GNS(Graph Network Simulator) 相关结构 / 接口
│ ├── orb.py # ORB 主体网络(encoder + heads)
│ └── utils.py # ORB 内部工具与辅助模块
├── finetune.py # 模型微调入口脚本
├── evaluate.py # 推理 / 评估入口脚本
├── run.sh # 单卡训练启动脚本(调用 finetune.py + config.yaml)
├── run_parallel.sh # 多卡并行训练启动脚本(msrun + config_parallel.yaml)
└── requirement.txt # Python 依赖列表(环境搭建用)

```

## 下载数据集


+ 1
- 1
MindChem/applications/orb/configs/config_eval.yaml View File

@@ -6,7 +6,7 @@ device_id: 0
val_data_path: dataset/val_mptrj_ase.db
num_workers: 8
batch_size: 64
checkpoint_path: orb_ckpts/orb-ft-checkpoint_epoch99.ckpt
checkpoint_path: orb_ckpts/orb-mptraj-only-v2.ckpt
random_seed: 1234
output_dir: results/



+ 1
- 1
MindChem/applications/orb/configs/config_parallel.yaml View File

@@ -2,7 +2,7 @@
train_data_path: dataset/train_mptrj_ase.db
val_data_path: dataset/val_mptrj_ase.db
num_workers: 8
batch_size: 256
batch_size: 64
gradient_clip_val: 0.5
max_epochs: 100
checkpoint_path: orb_ckpts/


+ 0
- 66
MindChem/applications/orb/mindchemistry/__init__.py View File

@@ -1,66 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Initialization for MindChemistry APIs."""

import time
import mindspore as ms
from mindspore import log as logger
from mindscience.e3nn import *
from .cell import *
from .utils import *
from .graph import *
from .so2_conv import *

__all__ = []
__all__.extend(cell.__all__)
__all__.extend(utils.__all__)



def _mindspore_version_check():
"""
Check MindSpore version for MindChemistry.

Raises:
ImportError: If MindSpore cannot be imported.
"""
try:
_ = ms.__version__
except ImportError as exc:
raise ImportError(
"Cannot find MindSpore in the current environment. Please install "
"MindSpore before using MindChemistry, by following the instruction at "
"https://www.mindspore.cn/install"
) from exc

ms_version = ms.__version__[:5]
required_mindspore_version = "1.8.1"

if ms_version < required_mindspore_version:
logger.warning(
f"Current version of MindSpore ({ms_version}) is not compatible with MindChemistry. "
f"Some functions might not work or even raise errors. Please install MindSpore "
f"version >= {required_mindspore_version}. For more details about dependency settings, "
f"please check the instructions at the MindSpore official website "
f"https://www.mindspore.cn/install or check the README.md at "
f"https://gitee.com/mindspore/mindscience"
)

for i in range(3, 0, -1):
logger.warning(f"Please pay attention to the above warning, countdown: {i}")
time.sleep(1)


_mindspore_version_check()

+ 0
- 22
MindChem/applications/orb/mindchemistry/cell/__init__.py View File

@@ -1,22 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""initialization for cells"""
from .basic_block import AutoEncoder, FCNet, MLPNet
from .orb import *

__all__ = [
'AutoEncoder', 'FCNet', 'MLPNet'
]
__all__.extend(orb.__all__)

+ 0
- 38
MindChem/applications/orb/mindchemistry/cell/activation.py View File

@@ -1,38 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""get activation function."""
from __future__ import absolute_import

from mindspore import ops
from mindspore.nn.layer import activation

_activation = {
'softmax': activation.Softmax,
'logsoftmax': activation.LogSoftmax,
'relu': activation.ReLU,
'silu': activation.SiLU,
'relu6': activation.ReLU6,
'tanh': activation.Tanh,
'gelu': activation.GELU,
'fast_gelu': activation.FastGelu,
'elu': activation.ELU,
'sigmoid': activation.Sigmoid,
'prelu': activation.PReLU,
'leakyrelu': activation.LeakyReLU,
'hswish': activation.HSwish,
'hsigmoid': activation.HSigmoid,
'logsigmoid': activation.LogSigmoid,
'sin': ops.Sin
}

+ 0
- 600
MindChem/applications/orb/mindchemistry/cell/basic_block.py View File

@@ -1,600 +0,0 @@
# Copyright 2023 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""basic"""
from __future__ import absolute_import

from collections.abc import Sequence
from typing import Union

from mindspore import nn
from mindspore.nn.layer import activation
from mindspore import ops, float16, float32, Tensor
from mindspore.common.initializer import Initializer

from .activation import _activation


def _get_dropout(dropout_rate):
"""
Gets the dropout functions.

Inputs:
dropout_rate (Union[int, float]): The dropout rate of the dropout function.
If dropout_rate was int or not in range (0,1], it would be rectify to closest float value.

Returns:
Function, the dropout function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_dropout
>>> dropout = get_dropout(0.5)
>>> dropout.set_train
Dropout<keep_prob=0.5>
"""
dropout_rate = float(max(min(dropout_rate, 1.), 1e-7))
return nn.Dropout(keep_prob=dropout_rate)


def _get_layernorm(channel, epsilon):
"""
Gets the layer normalization functions.

Inputs:
channel (Union[int, list]): The normalized shape of the layer normalization function.
If channel was int, it would be wrap into a list.
epsilon (float): The epsilon of the layer normalization function.

Returns:
Function, the layer normalization function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layernorm
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> layernorm = get_layernorm([2], 1e-7)
>>> output = layernorm(input_x)
>>> print(output)
[[ 9.99999881e-01, -9.99999881e-01],
[-1.00000000e+00, 1.00000000e+00]]
"""
if isinstance(channel, int):
channel = [channel]
return nn.LayerNorm(channel, epsilon=epsilon)


def _get_activation(name):
"""
Gets the activation function.

Inputs:
name (Union[str, None]): The name of the activation function. If name was None, it would return [].

Returns:
Function, the activation function.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_activation
>>> from mindspore import Tensor
>>> input_x = Tensor(np.array([[1.2, 0.1], [0.2, 3.2]], dtype=np.float32))
>>> sigmoid = _get_activation('sigmoid')
>>> output = sigmoid(input_x)
>>> print(output)
[[0.7685248 0.5249792 ]
[0.54983395 0.96083426]]
"""
if name is None:
return []
if isinstance(name, str):
name = name.lower()
if name not in _activation:
return activation.get_activation(name)
return _activation.get(name)()
return name


def _get_layer_arg(arguments, index):
"""
Gets the argument of each network layers.

Inputs:
arguments (Union[str, int, float, List, None]): The arguments of each layers.
If arguments was List return the argument at the index of the List.
index (int): The index of layer in the network

Returns:
Argument of the indexed layer.

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import _get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = _get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = _get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
if isinstance(arguments, list):
if len(arguments) <= index:
if len(arguments) == 1:
return [] if arguments[0] is None else arguments[0]
return []
return [] if arguments[index] is None else arguments[index]
return [] if arguments is None else arguments


def get_linear_block(
in_channels,
out_channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
"""
Gets the linear block list.

Inputs:
in_channels (int): The number of input channel.
out_channels (int): The number of output channel.
weight_init (Union[str, float, mindspore.common.initializer]): The initializer of the weights of dense layer
has_bias (bool): The switch for whether dense layer has bias.
bias_init (Union[str, float, mindspore.common.initializer]): The initializer of the bias of dense layer
has_dropout (bool): The switch for whether linear block has a dropout layer.
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
has_layernorm (bool): The switch for whether linear block has a layer normalization layer.
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
has_activation (bool): The switch for whether linear block has an activation layer.
act (Union[str, None]): The activation function in linear block

Returns:
List of mindspore.nn.Cell, linear block list .

Supported Platforms:
``Ascend`` ``GPU``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import get_layer_arg
>>> from mindspore import Tensor
>>> dropout_rate = get_layer_arg([0.1, 0.2, 0.3], index=2)
>>> print(dropout_rate)
0.2
>>> dropout_rate = get_layer_arg(0.2, index=2)
>>> print(dropout_rate)
0.2
"""
dense = nn.Dense(
in_channels, out_channels, weight_init=weight_init, bias_init=bias_init, has_bias=has_bias, activation=None
)
dropout = _get_dropout(dropout_rate) if (has_dropout is True) else []
layernorm = _get_layernorm(out_channels, layernorm_epsilon) if (has_layernorm is True) else []
act = _get_activation(act) if (has_activation is True) else []
block_list = [dense, dropout, layernorm, act]
while [] in block_list:
block_list.remove([])
return block_list


class FCNet(nn.Cell):
r"""
The Fully Connected Network. Applies a series of fully connected layers to the incoming data.

Args:
channels (List): the list of numbers of channel of each fully connected layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = FCNet([3, 16, 32, 16, 8])
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.has_bias = has_bias
self.bias_init = bias_init
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.layernorm_epsilon = layernorm_epsilon
self.has_activation = has_activation
self.activation = act
self.network = nn.SequentialCell(self._create_network())

def _create_network(self):
""" create the network """
cell_list = []
for i in range(len(self.channels) - 1):
cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return cell_list

def construct(self, x):
return self.network(x)


class MLPNet(nn.Cell):
r"""
The MLPNet Network. Applies a series of fully connected layers to the incoming data among which hidden layers have
same number of channels.

Args:
in_channels (int): the number of input layer channel.
out_channels (int): the number of output layer channel.
layers (int): the number of layers.
neurons (int): the number of channels of hidden layers.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer weights.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): The initializer of the bias of dense
layer. If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1] .
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .

Inputs:
- **input** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **output** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry.cell import FCNet
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = MLPNet(in_channels=3, out_channels=8, layers=5, neurons=32)
>>> output = net(inputs)
>>> print(output.shape)
(2, 8)

"""

def __init__(
self,
in_channels,
out_channels,
layers,
neurons,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu'
):
super().__init__()
self.channels = (in_channels,) + (layers - 2) * \
(neurons,) + (out_channels,)
self.network = FCNet(
channels=self.channels,
weight_init=weight_init,
has_bias=has_bias,
bias_init=bias_init,
has_dropout=has_dropout,
dropout_rate=dropout_rate,
has_layernorm=has_layernorm,
layernorm_epsilon=layernorm_epsilon,
has_activation=has_activation,
act=act
)

def construct(self, x):
return self.network(x)


class MLPMixPrecision(nn.Cell):
"""MLPMixPrecision
"""

def __init__(
self,
input_dim: int,
hidden_dims: Sequence,
short_cut=False,
batch_norm=False,
activation_fn='relu',
has_bias=False,
weight_init: Union[Initializer, str] = 'xavier_uniform',
bias_init: Union[Initializer, str] = 'zeros',
dropout=0,
dtype=float32
):
super().__init__()
self.dtype = dtype
self.div = ops.Div()

self.dims = [input_dim] + hidden_dims
self.short_cut = short_cut
self.nonlinear_const = 1.0
if isinstance(activation_fn, str):
self.activation = _activation.get(activation_fn)()
if activation_fn is not None and activation_fn == 'silu':
self.nonlinear_const = 1.679177
else:
self.activation = activation_fn
self.dropout = None
if dropout:
self.dropout = nn.Dropout(dropout)
fcs = [
nn.Dense(dim, self.dims[i + 1], weight_init=weight_init, bias_init=bias_init,
has_bias=has_bias).to_float(self.dtype) for i, dim in enumerate(self.dims[:-1])
]
self.layers = nn.CellList(fcs)
self.batch_norms = None
if batch_norm:
bns = [nn.BatchNorm1d(dim) for dim in self.dims[1:-1]]
self.batch_norms = nn.CellList(bns)

def construct(self, inputs):
"""construct

Args:
inputs: inputs

Returns:
inputs
"""
hidden = inputs
norm_from_last = 1.0
for i, layer in enumerate(self.layers):
sqrt_dim = ops.sqrt(Tensor(float(self.dims[i])))
layer_hidden = layer(hidden)
if self.dtype == float16:
layer_hidden = layer_hidden.astype(float16)
hidden = self.div(layer_hidden * norm_from_last, sqrt_dim)
norm_from_last = self.nonlinear_const
if i < len(self.layers) - 1:
if self.batch_norms is not None:
x = hidden.flatten(0, -2)
hidden = self.batch_norms[i](x).view_as(hidden)
if self.activation is not None:
hidden = self.activation(hidden)
if self.dropout is not None:
hidden = self.dropout(hidden)
if self.short_cut and hidden.shape == hidden.shape:
hidden += inputs
return hidden


class AutoEncoder(nn.Cell):
r"""
The AutoEncoder Network.
Applies an encoder to get the latent code and applies a decoder to get the reconstruct data.

Args:
channels (list): The number of channels of each encoder and decoder layer.
weight_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If weight_init was List, each element corresponds to each layer. Default: ``'normal'`` .
has_bias (Union[bool, List]): The switch for whether the dense layers has bias.
If has_bias was List, each element corresponds to each dense layer. Default: ``True`` .
bias_init (Union[str, float, mindspore.common.initializer, List]): initialize layer parameters.
If bias_init was List, each element corresponds to each dense layer. Default: ``'zeros'`` .
has_dropout (Union[bool, List]): The switch for whether linear block has a dropout layer.
If has_dropout was List, each element corresponds to each layer. Default: ``False`` .
dropout_rate (float): The dropout rate for dropout layer, the dropout rate must be a float in range (0, 1]
If dropout_rate was List, each element corresponds to each dropout layer. Default: ``0.5`` .
has_layernorm (Union[bool, List]): The switch for whether linear block has a layer normalization layer.
If has_layernorm was List, each element corresponds to each layer. Default: ``False`` .
layernorm_epsilon (float): The hyper parameter epsilon for layer normalization layer.
If layernorm_epsilon was List, each element corresponds to each layer normalization layer.
Default: ``1e-7`` .
has_activation (Union[bool, List]): The switch for whether linear block has an activation layer.
If has_activation was List, each element corresponds to each layer. Default: ``True`` .
act (Union[str, None, List]): The activation function in linear block.
If act was List, each element corresponds to each activation layer. Default: ``'relu'`` .
out_act (Union[None, str, mindspore.nn.Cell]): The activation function to output layer. Default: ``None`` .

Inputs:
- **x** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Outputs:
- **latents** (Tensor) - The shape of Tensor is :math:`(*, channels[-1])`.
- **x_recon** (Tensor) - The shape of Tensor is :math:`(*, channels[0])`.

Supported Platforms:
``Ascend``

Examples:
>>> import numpy as np
>>> from mindchemistry import AutoEncoder
>>> from mindspore import Tensor
>>> inputs = Tensor(np.array([[180, 234, 154], [244, 48, 247]], np.float32))
>>> net = AutoEncoder([3, 6, 2])
>>> output = net(inputs)
>>> print(output[0].shape, output[1].shape)
(2, 2) (2, 3)

"""

def __init__(
self,
channels,
weight_init='normal',
has_bias=True,
bias_init='zeros',
has_dropout=False,
dropout_rate=0.5,
has_layernorm=False,
layernorm_epsilon=1e-7,
has_activation=True,
act='relu',
out_act=None
):
super().__init__()
self.channels = channels
self.weight_init = weight_init
self.bias_init = bias_init
self.has_bias = has_bias
self.has_dropout = has_dropout
self.dropout_rate = dropout_rate
self.has_layernorm = has_layernorm
self.has_activation = has_activation
self.layernorm_epsilon = layernorm_epsilon
self.activation = act
self.output_activation = out_act
self.encoder = nn.SequentialCell(self._create_encoder())
self.decoder = nn.SequentialCell(self._create_decoder())

def _create_encoder(self):
""" create the network encoder """
encoder_cell_list = []
for i in range(len(self.channels) - 1):
encoder_cell_list += get_linear_block(
self.channels[i],
self.channels[i + 1],
weight_init=_get_layer_arg(self.weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(self.bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
return encoder_cell_list

def _create_decoder(self):
""" create the network decoder """
decoder_channels = self.channels[::-1]
decoder_weight_init = self.weight_init[::-1] if isinstance(self.weight_init, list) else self.weight_init
decoder_bias_init = self.bias_init[::-1] if isinstance(self.bias_init, list) else self.bias_init
decoder_cell_list = []
for i in range(len(decoder_channels) - 1):
decoder_cell_list += get_linear_block(
decoder_channels[i],
decoder_channels[i + 1],
weight_init=_get_layer_arg(decoder_weight_init, i),
has_bias=_get_layer_arg(self.has_bias, i),
bias_init=_get_layer_arg(decoder_bias_init, i),
has_dropout=_get_layer_arg(self.has_dropout, i),
dropout_rate=_get_layer_arg(self.dropout_rate, i),
has_layernorm=_get_layer_arg(self.has_layernorm, i),
layernorm_epsilon=_get_layer_arg(self.layernorm_epsilon, i),
has_activation=_get_layer_arg(self.has_activation, i),
act=_get_layer_arg(self.activation, i)
)
if self.output_activation is not None:
decoder_cell_list.append(_get_activation(self.output_activation))
return decoder_cell_list

def encode(self, x):
return self.encoder(x)

def decode(self, z):
return self.decoder(z)

def construct(self, x):
latents = self.encode(x)
x_recon = self.decode(latents)
return x_recon, latents

+ 0
- 120
MindChem/applications/orb/mindchemistry/cell/convolution.py View File

@@ -1,120 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""convolution"""
from mindspore import nn, ops, float32
from ..graph.graph import AggregateEdgeToNode
from ..e3.o3 import TensorProduct, Irreps, Linear
from ..e3.nn import FullyConnectedNet

softplus = ops.Softplus()


def shift_softplus(x):
return softplus(x) - 0.6931471805599453


def silu(x):
return x * ops.sigmoid(x)


class Convolution(nn.Cell):
r"""
InteractionBlock.

Args:
irreps_node_input: Input Features, default = None
irreps_node_attr: Nodes attribute irreps
irreps_node_output: Output irreps, in our case typically a single scalar
irreps_edge_attr: Edge attribute irreps
invariant_layers: Number of invariant layers, default = 1
invariant_neurons: Number of hidden neurons in invariant function, default = 8
avg_num_neighbors: Number of neighbors to divide by, default None => no normalization.
use_sc(bool): use self-connection or not
"""

def __init__(self,
irreps_node_input,
irreps_node_attr,
irreps_node_output,
irreps_edge_attr,
irreps_edge_scalars,
invariant_layers=1,
invariant_neurons=8,
avg_num_neighbors=None,
use_sc=True,
nonlin_scalars=None,
dtype=float32,
ncon_dtype=float32):
super().__init__()
self.avg_num_neighbors = avg_num_neighbors
self.use_sc = use_sc

self.irreps_node_input = Irreps(irreps_node_input)
self.irreps_node_attr = Irreps(irreps_node_attr)
self.irreps_node_output = Irreps(irreps_node_output)
self.irreps_edge_attr = Irreps(irreps_edge_attr)
self.irreps_edge_scalars = Irreps([(irreps_edge_scalars.num_irreps, (0, 1))])

self.lin1 = Linear(self.irreps_node_input, self.irreps_node_input, dtype=dtype, ncon_dtype=ncon_dtype)

tp = TensorProduct(self.irreps_node_input,
self.irreps_edge_attr,
self.irreps_node_output,
'merge',
weight_mode='custom',
dtype=dtype,
ncon_dtype=ncon_dtype)

self.fc = FullyConnectedNet([self.irreps_edge_scalars.num_irreps] + invariant_layers * [invariant_neurons] +
[tp.weight_numel], {
"ssp": shift_softplus,
"silu": ops.silu,
}.get(nonlin_scalars.get("e", None), None), dtype=dtype)

self.tp = tp
self.scatter = AggregateEdgeToNode(dim=1)

self.lin2 = Linear(tp.irreps_out.simplify(), self.irreps_node_output, dtype=dtype, ncon_dtype=ncon_dtype)

self.sc = None
if self.use_sc:
self.sc = TensorProduct(self.irreps_node_input,
self.irreps_node_attr,
self.irreps_node_output,
'connect',
dtype=dtype,
ncon_dtype=ncon_dtype)

def construct(self, node_input, node_attr, edge_src, edge_dst, edge_attr, edge_scalars):
"""Evaluate interaction Block with resnet"""
weight = self.fc(edge_scalars)

node_features = self.lin1(node_input)

edge_features = self.tp(node_features[edge_src], edge_attr, weight)

node_features = self.scatter(edge_attr=edge_features, edge_index=[edge_src, edge_dst],
dim_size=node_input.shape[0])

if self.avg_num_neighbors is not None:
node_features = node_features.div(self.avg_num_neighbors**0.5)

node_features = self.lin2(node_features)

if self.sc is not None:
sc = self.sc(node_input, node_attr)
node_features = node_features + sc

return node_features

+ 0
- 146
MindChem/applications/orb/mindchemistry/cell/embedding.py View File

@@ -1,146 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""embedding
"""
import math

import numpy as np
from mindspore import nn, ops, Tensor, Parameter, float32

from ..e3.o3 import Irreps


def _poly_cutoff(x, factor, p=6.0):
x = x * factor
out = 1.0
out = out - (((p + 1.0) * (p + 2.0) / 2.0) * ops.pow(x, p))
out = out + (p * (p + 2.0) * ops.pow(x, p + 1.0))
out = out - ((p * (p + 1.0) / 2) * ops.pow(x, p + 2.0))
return out * (x < 1.0)


class PolyCutoff(nn.Cell):

def __init__(self, r_max, p=6):
super().__init__()
self.p = float(p)
self._factor = 1.0 / float(r_max)

def construct(self, x):
return _poly_cutoff(x, self._factor, p=self.p)


class MaskPolynomialCutoff(nn.Cell):
"""MaskPolynomialCutoff
"""
_factor: float
p: float

def __init__(self, r_max: float, p: float = 6):
super().__init__()
self.p = float(p)
self._factor = 1.0 / float(r_max)
self.r_max = Tensor(r_max, dtype=float32)

self.cutoff = r_max

def construct(self, distance: Tensor, mask: Tensor = None):
decay = _poly_cutoff(distance, self._factor, p=self.p)

mask_lower = distance < self.cutoff
if mask is not None:
mask_lower &= mask

return decay, mask_lower


class BesselBasis(nn.Cell):
"""BesselBasis
"""

def __init__(self, r_max, num_basis=8, dtype=float32):
super().__init__()
self.r_max = r_max
self.num_basis = num_basis
self.prefactor = 2.0 / self.r_max
bessel_weights = Tensor(np.linspace(1., num_basis, num_basis) * math.pi, dtype=dtype)
self.bessel_weights = Parameter(bessel_weights)

def construct(self, x):
numerator = ops.sin(self.bessel_weights * x.unsqueeze(-1) / self.r_max)
return self.prefactor * (numerator / x.unsqueeze(-1))


class NormBesselBasis(nn.Cell):
"""NormBesselBasis
"""

def __init__(self, r_max, num_basis=8, norm_num=4000):
super().__init__()

self.basis = BesselBasis(r_max=r_max, num_basis=num_basis)
self.rs = Tensor(np.linspace(0.0, r_max, num=norm_num + 1), float32)[1:]

self.sqrt = ops.Sqrt()
self.sq = ops.Square()
self.div = ops.Div()

bessel_weights = Tensor(np.linspace(1.0, num_basis, num=num_basis), float32)

bessel_weights = bessel_weights * Tensor(math.pi, float32)
edge_length = self.rs
edge_length_unsqueeze = edge_length.unsqueeze(-1)
bessel_edge_length = bessel_weights * edge_length_unsqueeze
if r_max != 0:
bessel_edge_length = bessel_edge_length / r_max
prefactor = 2.0 / r_max
else:
raise ValueError
self.sin = ops.Sin()
numerator = self.sin(bessel_edge_length)
bs = prefactor * self.div(numerator, edge_length_unsqueeze)

basis_mean = Tensor(np.mean(bs.asnumpy(), axis=0), float32)
basis_std = self.sqrt(Tensor(np.mean(self.sq(bs - basis_mean).asnumpy(), 0), float32))
inv_std = ops.reciprocal(basis_std)

self.basis_mean = basis_mean
self.inv_std = inv_std

def construct(self, edge_length):
basis_length = self.basis(edge_length)
return (basis_length - self.basis_mean) * self.inv_std


class RadialEdgeEmbedding(nn.Cell):
"""RadialEdgeEmbedding
"""

def __init__(self, r_max, num_basis=8, p=6, dtype=float32):
super().__init__()
self.num_basis = num_basis
self.cutoff_p = p
self.basis = BesselBasis(r_max, num_basis, dtype=dtype)
self.cutoff = PolyCutoff(r_max, p)

self.irreps_out = Irreps([(self.basis.num_basis, (0, 1))])

def construct(self, edge_length):
edge_length_embedded = self.basis(edge_length) * self.cutoff(edge_length).unsqueeze(-1)
return edge_length_embedded

def __repr__(self):
return f'RadialEdgeEmbedding [num_basis: {self.num_basis}, cutoff_p: ' \
+ f'{self.cutoff_p}] ( -> {self.irreps_out} | {self.basis.num_basis} weights)'

+ 0
- 166
MindChem/applications/orb/mindchemistry/cell/message_passing.py View File

@@ -1,166 +0,0 @@
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""MessagePassing"""
from mindspore import nn, ops, float32

from mindscience.e3nn.o3 import Irreps
from mindscience.e3nn.nn import Gate, NormActivation
from .convolution import Convolution, shift_softplus

acts = {
"abs": ops.abs,
"tanh": ops.tanh,
"ssp": shift_softplus,
"silu": ops.silu,
}


class Compose(nn.Cell):
def __init__(self, first, second):
super().__init__()
self.first = first
self.second = second

def construct(self, *inputs):
x = self.first(*inputs)
x = self.second(x)
return x


class MessagePassing(nn.Cell):
"""MessagePassing"""
# pylint: disable=W0102
def __init__(
self,
irreps_node_input,
irreps_node_attr,
irreps_node_hidden,
irreps_node_output,
irreps_edge_attr,
irreps_edge_scalars,
convolution_kwargs={},
num_layers=3,
resnet=False,
nonlin_type="gate",
nonlin_scalars={"e": "ssp", "o": "tanh"},
nonlin_gates={"e": "ssp", "o": "abs"},
dtype=float32,
ncon_dtype=float32
):
super().__init__()
if nonlin_type not in ('gate', 'norm'):
raise ValueError(f"Unexpected nonlin_type {nonlin_type}.")

nonlin_scalars = {
1: nonlin_scalars["e"],
-1: nonlin_scalars["o"],
}
nonlin_gates = {
1: nonlin_gates["e"],
-1: nonlin_gates["o"],
}

self.irreps_node_input = Irreps(irreps_node_input)
self.irreps_node_hidden = Irreps(irreps_node_hidden)
self.irreps_node_output = Irreps(irreps_node_output)
self.irreps_node_attr = Irreps(irreps_node_attr)
self.irreps_edge_attr = Irreps(irreps_edge_attr)
self.irreps_edge_scalars = Irreps(irreps_edge_scalars)

irreps_node = self.irreps_node_input
irreps_prev = irreps_node
self.layers = nn.CellList()
self.resnets = []

for _ in range(num_layers):
tmp_irreps = irreps_node * self.irreps_edge_attr

irreps_scalars = Irreps(
[
(mul, ir)
for mul, ir in self.irreps_node_hidden
if ir.l == 0 and ir in tmp_irreps
]
).simplify()
irreps_gated = Irreps(
[
(mul, ir)
for mul, ir in self.irreps_node_hidden
if ir.l > 0 and ir in tmp_irreps
]
)

if nonlin_type == "gate":
ir = "0e" if Irreps("0e") in tmp_irreps else "0o"
irreps_gates = Irreps([(mul, ir)
for mul, _ in irreps_gated]).simplify()

nonlinear = Gate(
irreps_scalars,
[acts[nonlin_scalars[ir.p]] for _, ir in irreps_scalars],
irreps_gates,
[acts[nonlin_gates[ir.p]] for _, ir in irreps_gates],
irreps_gated,
dtype=dtype,
ncon_dtype=ncon_dtype
)

conv_irreps_out = nonlinear.irreps_in
else:
conv_irreps_out = (irreps_scalars + irreps_gated).simplify()

nonlinear = NormActivation(
irreps_in=conv_irreps_out,
act=acts[nonlin_scalars[1]],
normalize=True,
epsilon=1e-8,
bias=False,
dtype=dtype,
ncon_dtype=ncon_dtype
)

conv = Convolution(
irreps_node_input=irreps_node,
irreps_node_attr=self.irreps_node_attr,
irreps_node_output=conv_irreps_out,
irreps_edge_attr=self.irreps_edge_attr,
irreps_edge_scalars=self.irreps_edge_scalars,
**convolution_kwargs,
dtype=dtype,
ncon_dtype=ncon_dtype
)
irreps_node = nonlinear.irreps_out

self.layers.append(Compose(conv, nonlinear))

if irreps_prev == irreps_node and resnet:
self.resnets.append(True)
else:
self.resnets.append(False)
irreps_prev = irreps_node

def construct(self, node_input, node_attr, edge_src, edge_dst, edge_attr, edge_scalars):
"""construct"""
layer_in = node_input
for i in enumerate(self.layers):
layer_out = self.layers[i](
layer_in, node_attr, edge_src, edge_dst, edge_attr, edge_scalars)

if self.resnets[i]:
layer_in = layer_out + layer_in
else:
layer_in = layer_out

return layer_in

+ 0
- 15
MindChem/applications/orb/mindchemistry/graph/__init__.py View File

@@ -1,15 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""graph"""

+ 0
- 408
MindChem/applications/orb/mindchemistry/graph/dataloader.py View File

@@ -1,408 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""dataloader
"""
import random
import numpy as np
from mindspore import Tensor
import mindspore as ms


class DataLoaderBase:
r"""
DataLoader that stacks a batch of graph data to fixed-size Tensors

For specific dataset, usually the following functions should be customized to include different fields:
__init__, shuffle_action, __iter__

"""

def __init__(self,
batch_size,
edge_index,
label=None,
node_attr=None,
edge_attr=None,
padding_std_ratio=3.5,
dynamic_batch_size=True,
shuffle_dataset=True,
max_node=None,
max_edge=None):
self.batch_size = batch_size
self.edge_index = edge_index
self.index = 0
self.step = 0
self.padding_std_ratio = padding_std_ratio
self.batch_change_num = 0
self.batch_exceeding_num = 0
self.dynamic_batch_size = dynamic_batch_size
self.shuffle_dataset = shuffle_dataset

### can be customized to specific dataset
self.label = label
self.node_attr = node_attr
self.edge_attr = edge_attr
self.sample_num = len(self.node_attr)
batch_size_div = self.batch_size
if batch_size_div != 0:
self.step_num = int(self.sample_num / batch_size_div)
else:
raise ValueError

if dynamic_batch_size:
self.max_start_sample = self.sample_num
else:
self.max_start_sample = self.sample_num - self.batch_size + 1

self.set_global_max_node_edge_num(self.node_attr, self.edge_attr, max_node, max_edge, shuffle_dataset,
dynamic_batch_size)
#######

def __len__(self):
return self.sample_num

### example of generating data of each step, can be customized to specific dataset
def __iter__(self):
if self.shuffle_dataset:
self.shuffle()
else:
self.restart()

while self.index < self.max_start_sample:
# pylint: disable=W0612
edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num, batch_size \
= self.gen_common_data(self.node_attr, self.edge_attr)

### can be customized to generate different attributes or labels according to specific dataset
node_attr_step = self.gen_node_attr(self.node_attr, batch_size, node_num)
edge_attr_step = self.gen_edge_attr(self.edge_attr, batch_size, edge_num)
label_step = self.gen_global_attr(self.label, batch_size)

self.add_step_index(batch_size)

### make number to Tensor, if it is used as a Tensor in the network
node_num = Tensor(node_num)
batch_size = Tensor(batch_size)

yield node_attr_step, edge_attr_step, label_step, edge_index_step, node_batch_step, \
node_mask, edge_mask, node_num, batch_size

@staticmethod
def pad_zero_to_end(src, axis, zeros_len):
"""pad_zero_to_end"""
pad_shape = []
for i in range(src.ndim):
if i == axis:
pad_shape.append((0, zeros_len))
else:
pad_shape.append((0, 0))
return np.pad(src, pad_shape)

@staticmethod
def gen_mask(total_len, real_len):
"""gen_mask"""
mask = np.concatenate((np.full((real_len,), np.float32(1)), np.full((total_len - real_len,), np.float32(0))))
return mask

### example of computing global max length of node_attr and edge_attr, can be customized to specific dataset
def set_global_max_node_edge_num(self,
node_attr,
edge_attr,
max_node=None,
max_edge=None,
shuffle_dataset=True,
dynamic_batch_size=True):
"""set_global_max_node_edge_num

Args:
node_attr: node_attr
edge_attr: edge_attr
max_node: max_node. Defaults to None.
max_edge: max_edge. Defaults to None.
shuffle_dataset: shuffle_dataset. Defaults to True.
dynamic_batch_size: dynamic_batch_size. Defaults to True.

Raises:
ValueError: ValueError
"""
if not shuffle_dataset:
max_node_num, max_edge_num = self.get_max_node_edge_num(node_attr, edge_attr, dynamic_batch_size)
self.max_node_num_global = max_node_num if max_node is None else max(max_node, max_node_num)
self.max_edge_num_global = max_edge_num if max_edge is None else max(max_edge, max_edge_num)
return

sum_node = 0
sum_edge = 0
count = 0
max_node_single = 0
max_edge_single = 0
for step in range(self.sample_num):
node_len = len(node_attr[step])
edge_len = len(edge_attr[step])
sum_node += node_len
sum_edge += edge_len
max_node_single = max(max_node_single, node_len)
max_edge_single = max(max_edge_single, edge_len)
count += 1
if count != 0:
mean_node = sum_node / count
mean_edge = sum_edge / count
else:
raise ValueError

if max_node is not None and max_edge is not None:
if max_node < max_node_single:
raise ValueError(
f"the max_node {max_node} is less than the max length of a single sample {max_node_single}")
if max_edge < max_edge_single:
raise ValueError(
f"the max_edge {max_edge} is less than the max length of a single sample {max_edge_single}")

self.max_node_num_global = max_node
self.max_edge_num_global = max_edge
elif max_node is None and max_edge is None:
sum_node = 0
sum_edge = 0
for step in range(self.sample_num):
sum_node += (len(node_attr[step]) - mean_node) ** 2
sum_edge += (len(edge_attr[step]) - mean_edge) ** 2

if count != 0:
std_node = np.sqrt(sum_node / count)
std_edge = np.sqrt(sum_edge / count)
else:
raise ValueError

self.max_node_num_global = int(self.batch_size * mean_node +
self.padding_std_ratio * np.sqrt(self.batch_size) * std_node)
self.max_edge_num_global = int(self.batch_size * mean_edge +
self.padding_std_ratio * np.sqrt(self.batch_size) * std_edge)
self.max_node_num_global = max(self.max_node_num_global, max_node_single)
self.max_edge_num_global = max(self.max_edge_num_global, max_edge_single)
elif max_node is None:
if max_edge < max_edge_single:
raise ValueError(
f"the max_edge {max_edge} is less than the max length of a single sample {max_edge_single}")

if mean_edge != 0:
self.max_node_num_global = int(max_edge * mean_node / mean_edge)
else:
raise ValueError
self.max_node_num_global = max(self.max_node_num_global, max_node_single)
self.max_edge_num_global = max_edge
else:
if max_node < max_node_single:
raise ValueError(
f"the max_node {max_node} is less than the max length of a single sample {max_node_single}")

self.max_node_num_global = max_node
if mean_node != 0:
self.max_edge_num_global = int(max_node * mean_edge / mean_node)
else:
raise ValueError
self.max_edge_num_global = max(self.max_edge_num_global, max_edge_single)

def get_max_node_edge_num(self, node_attr, edge_attr, remainder=True):
"""get_max_node_edge_num

Args:
node_attr: node_attr
edge_attr: edge_attr
remainder (bool, optional): remainder. Defaults to True.

Returns:
max_node_num, max_edge_num
"""
max_node_num = 0
max_edge_num = 0
index = 0
for _ in range(self.step_num):
node_num = 0
edge_num = 0
for _ in range(self.batch_size):
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])
index += 1
max_node_num = max(max_node_num, node_num)
max_edge_num = max(max_edge_num, edge_num)

if remainder:
remain_num = self.sample_num - index - 1
node_num = 0
edge_num = 0
for _ in range(remain_num):
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])
index += 1
max_node_num = max(max_node_num, node_num)
max_edge_num = max(max_edge_num, edge_num)

return max_node_num, max_edge_num

def shuffle_index(self):
"""shuffle_index"""
indices = list(range(self.sample_num))
random.shuffle(indices)
return indices

### example of shuffling the input dataset, can be customized to specific dataset
def shuffle_action(self):
"""shuffle_action"""
indices = self.shuffle_index()
self.edge_index = [self.edge_index[i] for i in indices]
self.label = [self.label[i] for i in indices]
self.node_attr = [self.node_attr[i] for i in indices]
self.edge_attr = [self.edge_attr[i] for i in indices]

### example of generating the final shuffled dataset, can be customized to specific dataset
def shuffle(self):
"""shuffle"""
self.shuffle_action()
if not self.dynamic_batch_size:
max_node_num, max_edge_num = self.get_max_node_edge_num(self.node_attr, self.edge_attr, remainder=False)
while max_node_num > self.max_node_num_global or max_edge_num > self.max_edge_num_global:
self.shuffle_action()
max_node_num, max_edge_num = self.get_max_node_edge_num(self.node_attr, self.edge_attr, remainder=False)

self.step = 0
self.index = 0

def restart(self):
"""restart"""
self.step = 0
self.index = 0

### example of calculating dynamic batch size to avoid exceeding the max length of node and edge, can be customized to specific dataset
def get_batch_size(self, node_attr, edge_attr, start_batch_size):
"""get_batch_size

Args:
node_attr: node_attr
edge_attr: edge_attr
start_batch_size: start_batch_size

Returns:
batch_size
"""
node_num = 0
edge_num = 0
for i in range(start_batch_size):
index = self.index + i
node_num += len(node_attr[index])
edge_num += len(edge_attr[index])

exceeding = False
while node_num > self.max_node_num_global or edge_num > self.max_edge_num_global:
node_num -= len(node_attr[index])
edge_num -= len(edge_attr[index])
index -= 1
exceeding = True
self.batch_exceeding_num += 1
if exceeding:
self.batch_change_num += 1

return index - self.index + 1

def gen_common_data(self, node_attr, edge_attr):
"""gen_common_data

Args:
node_attr: node_attr
edge_attr: edge_attr

Returns:
common_data
"""
if self.dynamic_batch_size:
if self.step >= self.step_num:
batch_size = self.get_batch_size(node_attr, edge_attr,
min((self.sample_num - self.index), self.batch_size))
else:
batch_size = self.get_batch_size(node_attr, edge_attr, self.batch_size)
else:
batch_size = self.batch_size

######################## node_batch
node_batch_step = []
sample_num = 0
for i in range(self.index, self.index + batch_size):
node_batch_step.extend([sample_num] * node_attr[i].shape[0])
sample_num += 1
node_batch_step = np.array(node_batch_step)
node_num = node_batch_step.shape[0]

######################## edge_index
edge_index_step = np.array([[], []], dtype=np.int64)
max_edge_index = 0
for i in range(self.index, self.index + batch_size):
edge_index_step = np.concatenate((edge_index_step, self.edge_index[i] + max_edge_index), 1)
max_edge_index = np.max(edge_index_step) + 1
edge_num = edge_index_step.shape[1]

######################### padding
edge_index_step = self.pad_zero_to_end(edge_index_step, 1, self.max_edge_num_global - edge_num)
node_batch_step = self.pad_zero_to_end(node_batch_step, 0, self.max_node_num_global - node_num)

######################### mask
node_mask = self.gen_mask(self.max_node_num_global, node_num)
edge_mask = self.gen_mask(self.max_edge_num_global, edge_num)
batch_size_mask = self.gen_mask(self.batch_size, batch_size)

######################### make Tensor
edge_index_step = Tensor(edge_index_step, ms.int32)
node_batch_step = Tensor(node_batch_step, ms.int32)
node_mask = Tensor(node_mask)
edge_mask = Tensor(edge_mask)
batch_size_mask = Tensor(batch_size_mask)

return CommonData(edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size).get_tuple_data()

def gen_node_attr(self, node_attr, batch_size, node_num):
"""gen_node_attr"""
node_attr_step = np.concatenate(node_attr[self.index:self.index + batch_size], 0)
node_attr_step = self.pad_zero_to_end(node_attr_step, 0, self.max_node_num_global - node_num)
node_attr_step = Tensor(node_attr_step)
return node_attr_step

def gen_edge_attr(self, edge_attr, batch_size, edge_num):
"""gen_edge_attr"""
edge_attr_step = np.concatenate(edge_attr[self.index:self.index + batch_size], 0)
edge_attr_step = self.pad_zero_to_end(edge_attr_step, 0, self.max_edge_num_global - edge_num)
edge_attr_step = Tensor(edge_attr_step)
return edge_attr_step

def gen_global_attr(self, global_attr, batch_size):
"""gen_global_attr"""
global_attr_step = np.stack(global_attr[self.index:self.index + batch_size], 0)
global_attr_step = self.pad_zero_to_end(global_attr_step, 0, self.batch_size - batch_size)
global_attr_step = Tensor(global_attr_step)
return global_attr_step

def add_step_index(self, batch_size):
"""add_step_index"""
self.index = self.index + batch_size
self.step += 1

class CommonData:
"""CommonData"""
def __init__(self, edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size):
self.tuple_data = (edge_index_step, node_batch_step, node_mask, edge_mask, batch_size_mask, node_num, edge_num,
batch_size)

def get_tuple_data(self):
"""get_tuple_data"""
return self.tuple_data

+ 0
- 294
MindChem/applications/orb/mindchemistry/graph/graph.py View File

@@ -1,294 +0,0 @@
# Copyright 2024 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""graph"""
import mindspore as ms
from mindspore import ops, nn


def degree(index, dim_size, mask=None):
r"""
Computes the degree of a one-dimensional index tensor.
"""
if index.ndim != 1:
raise ValueError(f"the dimension of index {index.ndim} is not equal to 1")

if mask is not None:
if mask.shape[0] != index.shape[0]:
raise ValueError(f"mask.shape[0] {mask.shape[0]} is not equal to index.shape[0] {index.shape[0]}")
if mask.ndim != 1:
st = [0] * mask.ndim
slice_size = [1] * mask.ndim
slice_size[0] = mask.shape[0]
mask = ops.slice(mask, st, slice_size).squeeze()
src = mask.astype(ms.int32)
else:
src = ops.ones(index.shape, ms.int32)

index = index.unsqueeze(-1)
out = ops.zeros((dim_size,), ms.int32)

return ops.tensor_scatter_add(out, index, src)


class Aggregate(nn.Cell):
r"""
Easy-use version of scatter.

Args:
mode (str): {'add', 'sum', 'mean', 'avg'}, scatter mode.

Raises:
ValueError: If `mode` is not legal.

Supported Platforms:
``CPU`` ``GPU`` ``Ascend``

"""

def __init__(self, mode='add'):
super().__init__()
self.mode = mode
if mode in ('add', 'sum'):
self.scatter = self.scatter_sum
elif mode in ('mean', 'avg'):
self.scatter = self.scatter_mean
else:
raise ValueError(f"Unexpected scatter mode {mode}")

@staticmethod
def scatter_sum(src, index, out=None, dim_size=None, mask=None):
r"""
Computes the scatter sum of a source tensor. The index should be one-dimensional
"""
if index.ndim != 1:
raise ValueError(f"the dimension of index {index.ndim} is not equal to 1")
if index.shape[0] != src.shape[0]:
raise ValueError(f"index.shape[0] {index.shape[0]} is not equal to src.shape[0] {src.shape[0]}")
if out is None and dim_size is None:
raise ValueError("the out Tensor and out dim_size cannot be both None")

index = index.unsqueeze(-1)

if out is None:
out = ops.zeros((dim_size,) + src.shape[1:], dtype=src.dtype)
elif dim_size is not None and out.shape[0] != dim_size:
raise ValueError(f"the out.shape[0] {out.shape[0]} is not equal to dim_size {dim_size}")

if mask is not None:
if mask.shape[0] != src.shape[0]:
raise ValueError(f"mask.shape[0] {mask.shape[0]} is not equal to src.shape[0] {src.shape[0]}")
if src.ndim != mask.ndim:
if mask.size != mask.shape[0]:
raise ValueError("mask.ndim dose not match src.ndim, and cannot be broadcasted to the same")
shape = [1] * src.ndim
shape[0] = -1
mask = ops.reshape(mask, shape)
src = ops.mul(src, mask.astype(src.dtype))

return ops.tensor_scatter_add(out, index, src)

@staticmethod
def scatter_mean(src, index, out=None, dim_size=None, mask=None):
r"""
Computes the scatter mean of a source tensor. The index should be one-dimensional
"""
if out is None and dim_size is None:
raise ValueError("the out Tensor and out dim_size cannot be both None")

if dim_size is None:
dim_size = out.shape[0]
elif out is not None and out.shape[0] != dim_size:
raise ValueError(f"the out.shape[0] {out.shape[0]} is not equal to dim_size {dim_size}")

count = degree(index, dim_size, mask=mask)
eps = 1e-5
count = ops.maximum(count, eps)

scatter_sum = Aggregate.scatter_sum(src, index, dim_size=dim_size, mask=mask)

shape = [1] * scatter_sum.ndim
shape[0] = -1
count = ops.reshape(count, shape).astype(scatter_sum.dtype)
res = ops.true_divide(scatter_sum, count)

if out is not None:
res = res + out

return res


class AggregateNodeToGlobal(Aggregate):
"""AggregateNodeToGlobal"""

def __init__(self, mode='add'):
super().__init__(mode=mode)

def construct(self, node_attr, batch, out=None, dim_size=None, mask=None):
r"""
Args:
node_attr (Tensor): The source tensor of node attributes.
batch (Tensor): The indices of sample to scatter to.
out (Tensor): The destination tensor. Default: None.
dim_size (int): If `out` is not given, automatically create output with size `dim_size`. Default: None.
out and dim_size cannot be both None.
mask (Tensor): The mask of the node_attr tensor
Returns:
Tensor.
"""
return self.scatter(node_attr, batch, out=out, dim_size=dim_size, mask=mask)


class AggregateEdgeToGlobal(Aggregate):
"""AggregateEdgeToGlobal"""

def __init__(self, mode='add'):
super().__init__(mode=mode)

def construct(self, edge_attr, batch_edge, out=None, dim_size=None, mask=None):
r"""
Args:
edge_attr (Tensor): The source tensor of edge attributes.
batch_edge (Tensor): The indices of sample to scatter to.
out (Tensor): The destination tensor. Default: None.
dim_size (int): If `out` is not given, automatically create output with size `dim_size`. Default: None.
out and dim_size cannot be both None.
mask (Tensor): The mask of the node_attr tensor
Returns:
Tensor.
"""
return self.scatter(edge_attr, batch_edge, out=out, dim_size=dim_size, mask=mask)


class AggregateEdgeToNode(Aggregate):
"""AggregateEdgeToNode"""

def __init__(self, mode='add', dim=0):
super().__init__(mode=mode)
self.dim = dim

def construct(self, edge_attr, edge_index, out=None, dim_size=None, mask=None):
r"""
Args:
edge_attr (Tensor): The source tensor of edge attributes.
edge_index (Tensor): The indices of nodes in each edge.
out (Tensor): The destination tensor. Default: None.
dim_size (int): If `out` is not given, automatically create output with size `dim_size`. Default: None.
out and dim_size cannot be both None.
mask (Tensor): The mask of the node_attr tensor
Returns:
Tensor.
"""
return self.scatter(edge_attr, edge_index[self.dim], out=out, dim_size=dim_size, mask=mask)


class Lift(nn.Cell):
"""Lift"""

def __init__(self, mode="multi_graph"):
super().__init__()
self.mode = mode
if mode not in ["multi_graph", "single_graph"]:
raise ValueError(f"Unexpected lift mode {mode}")

@staticmethod
def lift(src, index, axis=0, mask=None):
"""lift"""
res = ops.index_select(src, axis, index)

if mask is not None:
if mask.shape[0] != res.shape[0]:
raise ValueError(f"mask.shape[0] {mask.shape[0]} is not equal to res.shape[0] {res.shape[0]}")
if res.ndim != mask.ndim:
if mask.size != mask.shape[0]:
raise ValueError("mask.ndim dose not match src.ndim, and cannot be broadcasted to the same")
shape = [1] * res.ndim
shape[0] = -1
mask = ops.reshape(mask, shape)
res = ops.mul(res, mask.astype(res.dtype))

return res

@staticmethod
def repeat(src, num, axis=0, max_len=None):
res = ops.repeat_elements(src, num, axis)

if (max_len is not None) and (max_len > num):
padding = ops.zeros((max_len - num,) + res.shape[1:], dtype=res.dtype)
res = ops.cat((res, padding), axis=0)

return res


class LiftGlobalToNode(Lift):
"""LiftGlobalToNode"""

def __init__(self, mode="multi_graph"):
super().__init__(mode=mode)

def construct(self, global_attr, batch=None, num_node=None, mask=None, max_len=None):
r"""
Args:
global_attr (Tensor): The source tensor of global attributes.
batch (Tensor): The indices of samples to get.
num_node (Int): The number of node in the graph, when there is only 1 graph.
mask (Tensor): The mask of the output tensor.
max_len (Int): The output length.
Returns:
Tensor.
"""
if global_attr.shape[0] > 1 or self.mode == "multi_graph":
return self.lift(global_attr, batch, mask=mask)
return self.repeat(global_attr, num_node, max_len=max_len)


class LiftGlobalToEdge(Lift):
"""LiftGlobalToEdge"""

def __init__(self, mode="multi_graph"):
super().__init__(mode=mode)

def construct(self, global_attr, batch_edge=None, num_edge=None, mask=None, max_len=None):
r"""
Args:
global_attr (Tensor): The source tensor of global attributes.
batch_edge (Tensor): The indices of samples to get.
num_edge (Int): The number of edge in the graph, when there is only 1 graph.
mask (Tensor): The mask of the output tensor.
max_len (Int): The output length.
Returns:
Tensor.
"""
if global_attr.shape[0] > 1 or self.mode == "multi_graph":
return self.lift(global_attr, batch_edge, mask=mask)
return self.repeat(global_attr, num_edge, max_len=max_len)


class LiftNodeToEdge(Lift):
"""LiftNodeToEdge"""

def __init__(self, dim=0):
super().__init__(mode="multi_graph")
self.dim = dim

def construct(self, global_attr, edge_index, mask=None):
r"""
Args:
global_attr (Tensor): The source tensor of global attributes.
edge_index (Tensor): The indices of nodes for each edge.
mask (Tensor): The mask of the output tensor.
Returns:
Tensor.
"""
return self.lift(global_attr, edge_index[self.dim], mask=mask)

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save
Baidu
map