gpse 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. gpse/__init__.py +11 -0
  2. gpse/batch/__init__.py +3 -0
  3. gpse/batch/cli.py +72 -0
  4. gpse/batch/runner.py +206 -0
  5. gpse/cli.py +215 -0
  6. gpse/config/__init__.py +15 -0
  7. gpse/config/_topsis_config.py +205 -0
  8. gpse/config/constants.py +127 -0
  9. gpse/config/default.yaml +15 -0
  10. gpse/config/software.yaml +20 -0
  11. gpse/config/topsis.yaml +18 -0
  12. gpse/convert/__init__.py +11 -0
  13. gpse/convert/cli.py +65 -0
  14. gpse/convert/external.py +400 -0
  15. gpse/convert/genotype_matrix.py +349 -0
  16. gpse/convert/phenotype.py +409 -0
  17. gpse/convert/processor.py +815 -0
  18. gpse/convert/qc.py +424 -0
  19. gpse/convert/validators.py +181 -0
  20. gpse/convert/workflow.py +214 -0
  21. gpse/models/__init__.py +30 -0
  22. gpse/models/classification_model_optimizer.py +393 -0
  23. gpse/models/classification_models.py +8 -0
  24. gpse/models/model_optimizers.py +14 -0
  25. gpse/models/regression_model_optimizer.py +679 -0
  26. gpse/predict/__init__.py +5 -0
  27. gpse/predict/__main__.py +3 -0
  28. gpse/predict/cli.py +95 -0
  29. gpse/predict/core.py +315 -0
  30. gpse/tasks/__init__.py +12 -0
  31. gpse/tasks/classification.py +565 -0
  32. gpse/tools/__init__.py +1 -0
  33. gpse/tools/analyze_phenotypes.py +140 -0
  34. gpse/train/__init__.py +26 -0
  35. gpse/train/_cv_manager.py +234 -0
  36. gpse/train/_data_io.py +261 -0
  37. gpse/train/_ensemble.py +235 -0
  38. gpse/train/_feature_selection.py +287 -0
  39. gpse/train/_fold_training.py +304 -0
  40. gpse/train/_model_pipeline.py +125 -0
  41. gpse/train/_model_tools.py +97 -0
  42. gpse/train/_optimization.py +244 -0
  43. gpse/train/_pipeline.py +490 -0
  44. gpse/train/_repeat_training.py +665 -0
  45. gpse/train/_results.py +262 -0
  46. gpse/train/cli.py +408 -0
  47. gpse/train/predictor.py +305 -0
  48. gpse/train/stacking.py +230 -0
  49. gpse/train/topsis.py +214 -0
  50. gpse/train/workflow.py +416 -0
  51. gpse/utils/__init__.py +54 -0
  52. gpse/utils/cli_display.py +529 -0
  53. gpse/utils/configuration.py +192 -0
  54. gpse/utils/dependency_checker.py +280 -0
  55. gpse/utils/feature_manifest.py +63 -0
  56. gpse/utils/genomic_utils.py +812 -0
  57. gpse/utils/log_utils.py +569 -0
  58. gpse/utils/logo.py +235 -0
  59. gpse/utils/paralle.py +266 -0
  60. gpse/utils/print_utils.py +75 -0
  61. gpse/utils/snp_ids.py +61 -0
  62. gpse/utils/version.py +159 -0
  63. gpse-0.0.2.dist-info/METADATA +1123 -0
  64. gpse-0.0.2.dist-info/RECORD +67 -0
  65. gpse-0.0.2.dist-info/WHEEL +4 -0
  66. gpse-0.0.2.dist-info/entry_points.txt +3 -0
  67. gpse-0.0.2.dist-info/licenses/LICENSE +21 -0
gpse/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """gpse — Genomic Prediction with Stacking Ensemble for horticultural crops."""
2
+
3
+ try:
4
+ from importlib.metadata import version as _get_version
5
+ except ImportError:
6
+ from importlib_metadata import version as _get_version # type: ignore[no-redef]
7
+
8
+ try:
9
+ __version__ = _get_version("gpse")
10
+ except Exception:
11
+ __version__ = "unknown"
gpse/batch/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """Batch training: run ``gpse train`` for many traits from one YAML config."""
gpse/batch/cli.py ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """Command-line interface for ``gpse batch``."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import argparse
8
+ import sys
9
+
10
+ _EPILOG = """\
11
+ YAML config schema:
12
+ defaults:
13
+ <any gpse train option>: value # inherited by every trait
14
+ results_root: /path/to/output # per-trait output = <results_root>/<name>
15
+ traits:
16
+ - name: FT # required; becomes --target_trait
17
+ task_type: classification # any train option may be overridden
18
+ n_classes: 3
19
+ models: [rf_clf, xgboost_clf]
20
+ results_dir: /custom/output # optional; overrides results_root
21
+ enabled: false # optional; skip this trait
22
+ """
23
+
24
+
25
+ def main(
26
+ argv: list[str] | None = None,
27
+ *,
28
+ formatter_class=None,
29
+ prog: str | None = None,
30
+ help_action=None,
31
+ parents: list[argparse.ArgumentParser] | None = None,
32
+ ) -> int:
33
+ """Run the GPSE batch training CLI."""
34
+ raw_args = list(sys.argv[1:] if argv is None else argv)
35
+ parser = argparse.ArgumentParser(
36
+ prog=prog or "python -m gpse.batch.cli",
37
+ description="Train GPSE models for multiple traits from one YAML config.",
38
+ formatter_class=formatter_class or argparse.RawDescriptionHelpFormatter,
39
+ epilog=_EPILOG,
40
+ parents=parents or [],
41
+ )
42
+ parser.add_argument(
43
+ "--config",
44
+ required=True,
45
+ help="Path to the YAML batch config file",
46
+ )
47
+ parser.add_argument(
48
+ "--dry_run",
49
+ action="store_true",
50
+ help="Print the generated 'gpse train' commands without running them",
51
+ )
52
+ args = parser.parse_args(raw_args)
53
+
54
+ # Initialize the standard GPSE console logger so batch messages share the
55
+ # same "[HH:MM:SS] INFO ..." style as `gpse train`. Without this, batch
56
+ # logs fall back to whatever handler was installed at import time (e.g.
57
+ # the rich_color_ext box sink pulled in transitively by the logo).
58
+ from gpse.utils.log_utils import logger_init
59
+
60
+ logger_init(log_level=getattr(args, "log_level", "INFO"))
61
+
62
+ from gpse.batch.runner import run_batch
63
+
64
+ try:
65
+ return run_batch(args.config, dry_run=args.dry_run)
66
+ except (FileNotFoundError, ValueError) as exc:
67
+ print(f"[ERROR] {exc}", file=sys.stderr)
68
+ return 1
69
+
70
+
71
+ if __name__ == "__main__":
72
+ sys.exit(main())
gpse/batch/runner.py ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Batch Training Runner
5
+ =====================
6
+ Loads a YAML batch config and runs ``gpse train`` once per trait.
7
+
8
+ Config schema::
9
+
10
+ defaults:
11
+ <any gpse train option>: value # inherited by every trait
12
+ results_root: /path/to/output # per-trait output = <results_root>/<name>
13
+ traits:
14
+ - name: FT # required; becomes --target_trait
15
+ task_type: classification # any train option may be overridden
16
+ n_classes: 3
17
+ models: [rf_clf, xgboost_clf]
18
+ results_dir: /custom/output # overrides results_root
19
+ enabled: false # optional; skip this trait
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import traceback
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ import yaml
30
+ from loguru import logger as main_logger
31
+
32
+ # Keys consumed by the batch runner itself; everything else must be a
33
+ # ``gpse train`` long option and is passed through.
34
+ RESERVED_KEYS = {"name", "enabled", "results_root", "target_trait"}
35
+
36
+
37
+ def _train_option_actions() -> dict[str, argparse.Action]:
38
+ """Map every ``gpse train`` long option name to its argparse action."""
39
+ from gpse.train.workflow import _build_parser
40
+
41
+ parser = _build_parser()
42
+ actions: dict[str, argparse.Action] = {}
43
+ for action in parser._actions:
44
+ for option in action.option_strings:
45
+ if option.startswith("--"):
46
+ actions[option[2:]] = action
47
+ return actions
48
+
49
+
50
+ def load_batch_config(config_path: str | Path) -> tuple[dict[str, Any], list[dict[str, Any]]]:
51
+ """Load and validate the YAML batch config; return (defaults, traits)."""
52
+ config_path = Path(config_path)
53
+ if not config_path.exists():
54
+ raise FileNotFoundError(f"Batch config not found: {config_path}")
55
+ with open(config_path, encoding="utf-8") as handle:
56
+ data = yaml.safe_load(handle)
57
+ if not isinstance(data, dict):
58
+ raise ValueError(f"Batch config must be a YAML mapping: {config_path}")
59
+
60
+ defaults = data.get("defaults") or {}
61
+ if not isinstance(defaults, dict):
62
+ raise ValueError("'defaults' must be a mapping of gpse train options")
63
+
64
+ traits = data.get("traits")
65
+ if not isinstance(traits, list) or not traits:
66
+ raise ValueError("'traits' must be a non-empty list")
67
+ for index, trait in enumerate(traits, start=1):
68
+ if not isinstance(trait, dict):
69
+ raise ValueError(f"traits entry #{index} must be a mapping")
70
+ name = trait.get("name")
71
+ if not name or not isinstance(name, str):
72
+ raise ValueError(f"traits entry #{index} is missing a string 'name'")
73
+ return defaults, traits
74
+
75
+
76
+ def build_trait_argv(
77
+ defaults: dict[str, Any],
78
+ trait: dict[str, Any],
79
+ actions: dict[str, argparse.Action] | None = None,
80
+ ) -> list[str]:
81
+ """Merge defaults with one trait entry and build a ``gpse train`` argv."""
82
+ actions = actions or _train_option_actions()
83
+ name = trait["name"]
84
+
85
+ if "target_trait" in defaults or "target_trait" in trait:
86
+ raise ValueError(
87
+ f"trait '{name}': 'target_trait' is derived from 'name'; do not set it directly"
88
+ )
89
+
90
+ merged = {**defaults, **{k: v for k, v in trait.items() if k not in RESERVED_KEYS}}
91
+ results_root = trait.get("results_root", defaults.get("results_root"))
92
+ merged.pop("results_root", None)
93
+
94
+ if not merged.get("results_dir"):
95
+ if not results_root:
96
+ raise ValueError(
97
+ f"trait '{name}': define 'results_dir' or set 'results_root' in defaults"
98
+ )
99
+ merged["results_dir"] = str(Path(str(results_root)) / name)
100
+ merged["target_trait"] = name
101
+
102
+ argv: list[str] = []
103
+ for key, value in merged.items():
104
+ if value is None:
105
+ continue
106
+ action = actions.get(key)
107
+ if action is None:
108
+ raise ValueError(
109
+ f"trait '{name}': unknown option '{key}' (not a 'gpse train' flag)"
110
+ )
111
+ flag = f"--{key}"
112
+ if isinstance(action, argparse.BooleanOptionalAction):
113
+ argv.append(flag if bool(value) else f"--no-{key}")
114
+ elif isinstance(action, argparse._StoreTrueAction):
115
+ if bool(value):
116
+ argv.append(flag)
117
+ elif action.nargs in ("+", "*"):
118
+ values = value if isinstance(value, (list, tuple)) else [value]
119
+ argv.append(flag)
120
+ argv.extend(str(item) for item in values)
121
+ else:
122
+ argv.append(flag)
123
+ argv.append(str(value))
124
+ return argv
125
+
126
+
127
+ def _format_train_command(argv: list[str]) -> str:
128
+ """Render a ``gpse train`` argv as a multi-line copy-pasteable command.
129
+
130
+ Each flag is grouped with its values on its own line, joined with shell
131
+ line-continuations, so long paths stay intact instead of wrapping
132
+ mid-token at the terminal edge.
133
+ """
134
+ groups: list[list[str]] = []
135
+ for token in argv:
136
+ if token.startswith("--") or not groups:
137
+ groups.append([token])
138
+ else:
139
+ groups[-1].append(token)
140
+ lines = ["gpse train \\"]
141
+ lines.extend(f" {' '.join(group)} \\" for group in groups[:-1])
142
+ lines.append(f" {' '.join(groups[-1])}")
143
+ return "\n".join(lines)
144
+
145
+
146
+ def run_batch(config_path: str | Path, dry_run: bool = False) -> int:
147
+ """Run ``gpse train`` for every enabled trait; return 0 when all succeed."""
148
+ defaults, traits = load_batch_config(config_path)
149
+ actions = _train_option_actions()
150
+
151
+ runnable = [trait for trait in traits if trait.get("enabled", True)]
152
+ skipped = [trait["name"] for trait in traits if not trait.get("enabled", True)]
153
+ if skipped:
154
+ main_logger.info(f"Skipping disabled traits: {skipped}")
155
+ if not runnable:
156
+ main_logger.error("No enabled traits to run")
157
+ return 1
158
+
159
+ main_logger.info(f"Batch training: {len(runnable)} trait(s) from {config_path}")
160
+ outcomes: list[tuple[str, str]] = []
161
+ for index, trait in enumerate(runnable, start=1):
162
+ name = trait["name"]
163
+ try:
164
+ argv = build_trait_argv(defaults, trait, actions)
165
+ except ValueError as exc:
166
+ main_logger.error(str(exc))
167
+ outcomes.append((name, "config-error"))
168
+ continue
169
+
170
+ if dry_run:
171
+ print(f"[{index}/{len(runnable)}] {name}")
172
+ print(_format_train_command(argv))
173
+ outcomes.append((name, "dry-run"))
174
+ continue
175
+
176
+ main_logger.info("=" * 70)
177
+ main_logger.info(f"[{index}/{len(runnable)}] Training trait: {name}")
178
+ main_logger.info("=" * 70)
179
+ from gpse.train.cli import main as train_main
180
+
181
+ try:
182
+ exit_code = train_main(argv, prog="gpse train")
183
+ except SystemExit as exc:
184
+ exit_code = exc.code if isinstance(exc.code, int) else 1
185
+ except Exception:
186
+ main_logger.error(f"Trait {name} failed with an unexpected error:")
187
+ main_logger.error(traceback.format_exc())
188
+ exit_code = 1
189
+
190
+ if exit_code == 130:
191
+ main_logger.warning("Batch interrupted by user (Ctrl+C).")
192
+ outcomes.append((name, "interrupted"))
193
+ _log_summary(outcomes)
194
+ return 130
195
+ outcomes.append((name, "ok" if exit_code == 0 else f"failed({exit_code})"))
196
+
197
+ _log_summary(outcomes)
198
+ return 0 if all(status in {"ok", "dry-run"} for _, status in outcomes) else 1
199
+
200
+
201
+ def _log_summary(outcomes: list[tuple[str, str]]) -> None:
202
+ main_logger.info("=" * 70)
203
+ main_logger.info("Batch summary:")
204
+ for name, status in outcomes:
205
+ main_logger.info(f" {name}: {status}")
206
+ main_logger.info("=" * 70)
gpse/cli.py ADDED
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ GPSE Command-Line Interface
5
+
6
+ This module is intentionally thin: it only defines argument parsers and routes
7
+ the execution flow. Training business logic lives in ``gpse.train``.
8
+ """
9
+
10
+ import os
11
+ import sys
12
+ import argparse
13
+
14
+ # Configure native thread pools before importing modules that may load
15
+ # numpy/scipy/sklearn. BLAS/MKL/OpenMP read these environment variables only
16
+ # when their pools are initialized, so this has to happen at the top.
17
+ _thread_pre_parser = argparse.ArgumentParser(add_help=False)
18
+ _thread_pre_parser.add_argument("--n_jobs", type=int, default=1)
19
+ _thread_pre_args, _ = _thread_pre_parser.parse_known_args()
20
+ _thread_n = str(_thread_pre_args.n_jobs)
21
+ for _env_var in (
22
+ "OMP_NUM_THREADS",
23
+ "MKL_NUM_THREADS",
24
+ "OPENBLAS_NUM_THREADS",
25
+ "NUMEXPR_NUM_THREADS",
26
+ "VECLIB_MAXIMUM_THREADS",
27
+ "BLIS_NUM_THREADS",
28
+ ):
29
+ os.environ[_env_var] = _thread_n
30
+ del _thread_pre_parser, _thread_pre_args, _thread_n, _env_var
31
+
32
+ # CLI display helpers are imported after thread setup because they can
33
+ # eventually touch package modules that import numerical dependencies.
34
+ from gpse.utils.cli_display import (
35
+ ULTIMATE_QUESTION,
36
+ _build_root_parser,
37
+ _show_logo_for_command,
38
+ print_easter_egg,
39
+ show_gpse_logo,
40
+ )
41
+
42
+ # Shared argparse action: prints the logo before root/subcommand help.
43
+ from gpse.config.constants import _LogoHelpAction
44
+
45
+ # Prefer rich-formatted help when available; keep argparse as fallback so the
46
+ # CLI still works in minimal environments.
47
+ try:
48
+ from rich_argparse import RichHelpFormatter
49
+ except ImportError:
50
+ RichHelpFormatter = argparse.HelpFormatter
51
+
52
+ # Console is optional. All user-facing error paths below have plain print()
53
+ # fallbacks for environments without rich.
54
+ try:
55
+ from rich.console import Console
56
+ _console = Console()
57
+ except ImportError:
58
+ _console = None
59
+
60
+ # Common root options are passed into subcommand parsers as parents. This keeps
61
+ # flags such as --version and --log-level consistent across gpse commands.
62
+ _common_parent = argparse.ArgumentParser(add_help=False)
63
+ _common_parent.add_argument(
64
+ "-v", "--version",
65
+ action="store_true",
66
+ help="Show version information and exit",
67
+ )
68
+ _common_parent.add_argument(
69
+ "-l", "--log-level",
70
+ default="INFO",
71
+ choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
72
+ help="Set logging level (default: INFO)",
73
+ )
74
+
75
+
76
+ def main(argv: list[str] | None = None) -> int:
77
+ """
78
+ Main entry point for the GPSE CLI.
79
+
80
+ argv is injectable for tests and internal calls. When argv is None, the
81
+ function reads the real command-line arguments from sys.argv.
82
+ """
83
+ # Normalize inputs to a list copy so downstream slicing and checks are
84
+ # stable even when tests pass tuples or other sequences.
85
+ raw_args = list(sys.argv[1:] if argv is None else argv)
86
+
87
+ # Build the lightweight root router. Detailed workflow arguments are
88
+ # defined inside gpse.convert.cli, gpse.train.cli, and gpse.predict.cli.
89
+ root_parser = _build_root_parser(
90
+ formatter_class=RichHelpFormatter,
91
+ parents=[_common_parent],
92
+ help_action=_LogoHelpAction,
93
+ )
94
+
95
+ # No subcommand was provided. Show the logo and root help, then fail with
96
+ # exit code 1 because this is an invalid invocation.
97
+ if not raw_args:
98
+ show_gpse_logo()
99
+ if _console is not None:
100
+ _console.print("\n[bold red][ERROR] No command provided. Use convert, train, predict, or batch.[/bold red]\n")
101
+ else:
102
+ print("\n[ERROR] No command provided. Use convert, train, predict, or batch.\n")
103
+ root_parser.print_help()
104
+ return 1
105
+
106
+ # Root help is handled by the root parser so the custom help action can
107
+ # print the logo before the help text.
108
+ if raw_args[0] in {"-h", "--help"}:
109
+ root_parser.parse_args(raw_args)
110
+ return 0
111
+
112
+ # Root version delegates to the train CLI because that module owns the
113
+ # current version display implementation.
114
+ if raw_args[0] in {"-v", "--version"}:
115
+ from gpse.train.cli import main as train_main
116
+
117
+ return train_main(["--version"], prog="gpse")
118
+
119
+ # Easter egg shortcuts are handled before subcommand dispatch, so commands
120
+ # like `gpse 42` do not get treated as unknown subcommands.
121
+ if len(raw_args) == 1 and raw_args[0] == "42":
122
+ print_easter_egg(show_question=False)
123
+ return 0
124
+
125
+ # Also accept the full question, ignoring case and trailing punctuation.
126
+ joined_args = " ".join(raw_args).strip().lower().rstrip("?!. ")
127
+ if joined_args == ULTIMATE_QUESTION:
128
+ print_easter_egg(show_question=True)
129
+ return 0
130
+
131
+ # Split root command from the arguments that belong to the selected
132
+ # workflow. The workflow modules receive only their own arguments.
133
+ command = raw_args[0]
134
+ command_args = raw_args[1:]
135
+
136
+ # Convert workflow: import lazily so gpse --help and gpse --version stay
137
+ # lightweight and do not load workflow dependencies unnecessarily.
138
+ if command == "convert":
139
+ # Display the logo before convert/predict help.
140
+ _show_logo_for_command(command_args)
141
+
142
+ from gpse.convert.cli import main as convert_main
143
+
144
+ # convert/predict do not own version rendering; route to the shared
145
+ # version implementation while preserving the command name in prog.
146
+ if command_args and command_args[0] in {"-v", "--version"}:
147
+ from gpse.train.cli import main as train_main
148
+
149
+ return train_main(["--version"], prog="gpse convert")
150
+ return convert_main(
151
+ command_args,
152
+ formatter_class=RichHelpFormatter,
153
+ prog="gpse convert",
154
+ help_action=_LogoHelpAction,
155
+ parents=[_common_parent],
156
+ )
157
+
158
+ # Train workflow.
159
+ if command == "train":
160
+ # Display the logo before convert/predict help.
161
+ _show_logo_for_command(command_args)
162
+
163
+ from gpse.train.cli import main as train_main
164
+
165
+ return train_main(
166
+ command_args,
167
+ formatter_class=RichHelpFormatter,
168
+ prog="gpse train",
169
+ help_action=_LogoHelpAction,
170
+ parents=[_common_parent],
171
+ )
172
+
173
+ # Predict workflow.
174
+ if command == "predict":
175
+ # Display the logo before convert/predict help.
176
+ _show_logo_for_command(command_args)
177
+
178
+ from gpse.predict.cli import main as predict_main
179
+
180
+ if command_args and command_args[0] in {"-v", "--version"}:
181
+ from gpse.train.cli import main as train_main
182
+
183
+ return train_main(["--version"], prog="gpse predict")
184
+ return predict_main(
185
+ command_args,
186
+ formatter_class=RichHelpFormatter,
187
+ prog="gpse predict",
188
+ help_action=_LogoHelpAction,
189
+ parents=[_common_parent],
190
+ )
191
+
192
+ # Batch workflow: train many traits from one YAML config.
193
+ if command == "batch":
194
+ _show_logo_for_command(command_args)
195
+
196
+ from gpse.batch.cli import main as batch_main
197
+
198
+ if command_args and command_args[0] in {"-v", "--version"}:
199
+ from gpse.train.cli import main as train_main
200
+
201
+ return train_main(["--version"], prog="gpse batch")
202
+ return batch_main(
203
+ command_args,
204
+ formatter_class=RichHelpFormatter,
205
+ prog="gpse batch",
206
+ parents=[_common_parent],
207
+ )
208
+
209
+ # Any other first token is not a supported workflow command.
210
+ root_parser.error(f"Unknown command: {command}. Use train, convert, predict, or batch.")
211
+ return 2
212
+
213
+
214
+ if __name__ == "__main__":
215
+ sys.exit(main())
@@ -0,0 +1,15 @@
1
+ """gpse.config — Configuration constants and model data classes."""
2
+
3
+ from .constants import (
4
+ ModelConstants,
5
+ ModelConfig,
6
+ ClassificationModelConfig,
7
+ NumpyEncoder,
8
+ )
9
+
10
+ __all__ = [
11
+ "ModelConstants",
12
+ "ModelConfig",
13
+ "ClassificationModelConfig",
14
+ "NumpyEncoder",
15
+ ]