factorforge-cds 3.1.4__tar.gz → 3.1.6__tar.gz
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.
- {factorforge_cds-3.1.4/src/factorforge_cds.egg-info → factorforge_cds-3.1.6}/PKG-INFO +12 -7
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/README.md +7 -5
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/pyproject.toml +9 -2
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/__init__.py +1 -1
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/analysis/metrics.py +3 -1
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/cli/main.py +117 -1
- factorforge_cds-3.1.6/src/factorforge/data/nbenthamiana_codons.json +528 -0
- factorforge_cds-3.1.6/src/factorforge/data/nbenthamiana_golden_set.json +100 -0
- factorforge_cds-3.1.6/src/factorforge/data/ntabacum_codons.json +528 -0
- factorforge_cds-3.1.6/src/factorforge/data/templates/high_expression.json +34 -0
- factorforge_cds-3.1.6/src/factorforge/data/templates/standard_expression.json +34 -0
- factorforge_cds-3.1.6/src/factorforge/data/wolffia_globosa_codons.json +522 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/database.py +1 -4
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/__init__.py +1 -1
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/__init__.py +1 -1
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/optimizer.py +15 -5
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/pipeline.py +32 -16
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/rules/domesticator.py +9 -6
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/rules/reverse_translator.py +4 -1
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/rules/rule_engine.py +8 -2
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/scoring.py +33 -5
- factorforge_cds-3.1.6/src/factorforge/engines/profile/scoring_ml.py +208 -0
- factorforge_cds-3.1.6/src/factorforge/schemas/design_package.schema.json +373 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6/src/factorforge_cds.egg-info}/PKG-INFO +12 -7
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge_cds.egg-info/SOURCES.txt +8 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge_cds.egg-info/requires.txt +4 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/LICENSE +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/setup.cfg +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/__main__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/analysis/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/analysis/feasibility.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/cli/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/cli/legacy_cli.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/core/interfaces/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/core/interfaces/exporter.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/core/interfaces/optimizer.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/core/interfaces/validator.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/construct_builder.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/exporter.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/rules/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/utils.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/profile/validator.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/engines/registry.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/schemas/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/schemas/design_package.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/construct_id.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/exceptions.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/restriction_sites.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/sequence_validator.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/utils/validation.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/validation/__init__.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/validation/cli.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge/validation/package_generator.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/src/factorforge_cds.egg-info/top_level.txt +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/tests/test_database.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/tests/test_legacy_cli.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/tests/test_restriction_sites.py +0 -0
- {factorforge_cds-3.1.4 → factorforge_cds-3.1.6}/tests/test_sequence_validator.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.6
|
|
4
4
|
Summary: FactorForge - open-source constraint-based CDS design engine by Eijex.
|
|
5
5
|
Author-email: Eijex <eijex.lab@gmail.com>
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
7
7
|
Project-URL: Homepage, https://factorforge-cds.vercel.app
|
|
8
8
|
Project-URL: Repository, https://github.com/eijex/factorforge-cds
|
|
9
9
|
Project-URL: Issues, https://github.com/eijex/factorforge-cds/issues
|
|
10
|
-
Keywords: codon optimization,CDS design,synthetic biology,bioinformatics,Nicotiana benthamiana,constraint optimization,dynamic programming
|
|
10
|
+
Keywords: codon optimization,CDS design,synthetic biology,bioinformatics,Nicotiana benthamiana,Nicotiana tabacum,Tobacco BY-2,constraint optimization,dynamic programming
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -24,19 +24,24 @@ Requires-Dist: pytest>=7.0; extra == "dev"
|
|
|
24
24
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
25
25
|
Requires-Dist: ruff>=0.1; extra == "dev"
|
|
26
26
|
Requires-Dist: pyyaml>=6.0; extra == "dev"
|
|
27
|
+
Provides-Extra: ml
|
|
28
|
+
Requires-Dist: transformers>=4.40; extra == "ml"
|
|
29
|
+
Requires-Dist: torch>=2.0; extra == "ml"
|
|
27
30
|
Dynamic: license-file
|
|
28
31
|
|
|
29
32
|
# FactorForge
|
|
30
33
|
|
|
31
|
-
**Open-source constraint-based CDS design engine for *Nicotiana benthamiana*
|
|
34
|
+
**Open-source constraint-based CDS design engine for plant expression workflows, with initial focus on *Nicotiana benthamiana* and Tobacco BY-2.**
|
|
32
35
|
|
|
33
36
|
[](LICENSE)
|
|
34
37
|
[](https://www.python.org/)
|
|
35
|
-
[](https://github.com/eijex/factorforge-cds/releases)
|
|
36
38
|
[](https://pypi.org/project/factorforge-cds/)
|
|
39
|
+
[](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml)
|
|
40
|
+
[](https://codecov.io/gh/eijex/factorforge-cds)
|
|
41
|
+
[](https://doi.org/10.5281/zenodo.20407331)
|
|
37
42
|
[](https://factorforge-cds.vercel.app)
|
|
38
43
|
|
|
39
|
-
FactorForge optimizes protein sequences into
|
|
44
|
+
FactorForge optimizes protein sequences into host-compatible CDS by maximizing CAI, controlling GC content, eliminating PolyA signals, and producing MoClo/Golden Gate-ready constructs. Supports *N. benthamiana* (agroinfiltration) and Tobacco BY-2 (`--host by2`, bioreactor/cGMP workflows).
|
|
40
45
|
|
|
41
46
|
**→ [Full Documentation](https://eijex.github.io/factorforge-cds/)**
|
|
42
47
|
|
|
@@ -86,7 +91,7 @@ FactorForge has gone through several implementation generations before the curre
|
|
|
86
91
|
| **v2** — Rule-Based Engine | Internal → Production | Deterministic, constraint-aware design engine; became the foundation for the public release |
|
|
87
92
|
| **v3-alpha** — ML Prototype | Archived | ML-based design attempt; performance was insufficient for production use; preserved under `archive/v3-ml-prototype/` |
|
|
88
93
|
| **v3.0+** — Current release | Public | Open-source release of the matured v2 engine under `factorforge.engines.profile` |
|
|
89
|
-
| **
|
|
94
|
+
| **v3.7+** — ML Engine | Planned | ML-based design as `--engine ml`; added once sufficient wet-lab data is available |
|
|
90
95
|
|
|
91
96
|
The `archive/` directory preserves all three earlier tracks for provenance. None are installed or exposed by the current package.
|
|
92
97
|
|
|
@@ -101,7 +106,7 @@ FactorForge predictions are **in-silico only** and have not been experimentally
|
|
|
101
106
|
## Citing
|
|
102
107
|
|
|
103
108
|
```
|
|
104
|
-
FactorForge v3.1.
|
|
109
|
+
FactorForge v3.1.6 (2026). Open-source constraint-based CDS design engine.
|
|
105
110
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
106
111
|
```
|
|
107
112
|
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# FactorForge
|
|
2
2
|
|
|
3
|
-
**Open-source constraint-based CDS design engine for *Nicotiana benthamiana*
|
|
3
|
+
**Open-source constraint-based CDS design engine for plant expression workflows, with initial focus on *Nicotiana benthamiana* and Tobacco BY-2.**
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.python.org/)
|
|
7
|
-
[](https://github.com/eijex/factorforge-cds/releases)
|
|
8
7
|
[](https://pypi.org/project/factorforge-cds/)
|
|
8
|
+
[](https://github.com/eijex/factorforge-cds/actions/workflows/ci.yml)
|
|
9
|
+
[](https://codecov.io/gh/eijex/factorforge-cds)
|
|
10
|
+
[](https://doi.org/10.5281/zenodo.20407331)
|
|
9
11
|
[](https://factorforge-cds.vercel.app)
|
|
10
12
|
|
|
11
|
-
FactorForge optimizes protein sequences into
|
|
13
|
+
FactorForge optimizes protein sequences into host-compatible CDS by maximizing CAI, controlling GC content, eliminating PolyA signals, and producing MoClo/Golden Gate-ready constructs. Supports *N. benthamiana* (agroinfiltration) and Tobacco BY-2 (`--host by2`, bioreactor/cGMP workflows).
|
|
12
14
|
|
|
13
15
|
**→ [Full Documentation](https://eijex.github.io/factorforge-cds/)**
|
|
14
16
|
|
|
@@ -58,7 +60,7 @@ FactorForge has gone through several implementation generations before the curre
|
|
|
58
60
|
| **v2** — Rule-Based Engine | Internal → Production | Deterministic, constraint-aware design engine; became the foundation for the public release |
|
|
59
61
|
| **v3-alpha** — ML Prototype | Archived | ML-based design attempt; performance was insufficient for production use; preserved under `archive/v3-ml-prototype/` |
|
|
60
62
|
| **v3.0+** — Current release | Public | Open-source release of the matured v2 engine under `factorforge.engines.profile` |
|
|
61
|
-
| **
|
|
63
|
+
| **v3.7+** — ML Engine | Planned | ML-based design as `--engine ml`; added once sufficient wet-lab data is available |
|
|
62
64
|
|
|
63
65
|
The `archive/` directory preserves all three earlier tracks for provenance. None are installed or exposed by the current package.
|
|
64
66
|
|
|
@@ -73,7 +75,7 @@ FactorForge predictions are **in-silico only** and have not been experimentally
|
|
|
73
75
|
## Citing
|
|
74
76
|
|
|
75
77
|
```
|
|
76
|
-
FactorForge v3.1.
|
|
78
|
+
FactorForge v3.1.6 (2026). Open-source constraint-based CDS design engine.
|
|
77
79
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
78
80
|
```
|
|
79
81
|
|
|
@@ -4,14 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "factorforge-cds"
|
|
7
|
-
version = "3.1.
|
|
7
|
+
version = "3.1.6"
|
|
8
8
|
description = "FactorForge - open-source constraint-based CDS design engine by Eijex."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-only"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
12
12
|
requires-python = ">=3.10"
|
|
13
13
|
authors = [{ name = "Eijex", email = "eijex.lab@gmail.com" }]
|
|
14
|
-
keywords = ["codon optimization", "CDS design", "synthetic biology", "bioinformatics", "Nicotiana benthamiana", "constraint optimization", "dynamic programming"]
|
|
14
|
+
keywords = ["codon optimization", "CDS design", "synthetic biology", "bioinformatics", "Nicotiana benthamiana", "Nicotiana tabacum", "Tobacco BY-2", "constraint optimization", "dynamic programming"]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
17
17
|
"Intended Audience :: Science/Research",
|
|
@@ -33,6 +33,10 @@ dev = [
|
|
|
33
33
|
"ruff>=0.1",
|
|
34
34
|
"pyyaml>=6.0",
|
|
35
35
|
]
|
|
36
|
+
ml = [
|
|
37
|
+
"transformers>=4.40",
|
|
38
|
+
"torch>=2.0",
|
|
39
|
+
]
|
|
36
40
|
|
|
37
41
|
[project.urls]
|
|
38
42
|
Homepage = "https://factorforge-cds.vercel.app"
|
|
@@ -46,6 +50,9 @@ factorforge-validate = "factorforge.validation.cli:main"
|
|
|
46
50
|
[tool.setuptools.packages.find]
|
|
47
51
|
where = ["src"]
|
|
48
52
|
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
"factorforge" = ["data/*.json", "data/templates/*.json", "schemas/*.json"]
|
|
55
|
+
|
|
49
56
|
[tool.pytest.ini_options]
|
|
50
57
|
testpaths = ["tests"]
|
|
51
58
|
norecursedirs = ["archive"]
|
|
@@ -9,6 +9,8 @@ from dataclasses import dataclass
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
+
from factorforge.engines.profile.utils import get_data_path
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
STANDARD_GENETIC_CODE: dict[str, str] = {
|
|
14
16
|
"TTT": "F",
|
|
@@ -90,7 +92,7 @@ class CodonUsageTable:
|
|
|
90
92
|
|
|
91
93
|
|
|
92
94
|
def _default_codon_table_path() -> Path:
|
|
93
|
-
return
|
|
95
|
+
return get_data_path() / "nbenthamiana_codons.json"
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def load_codon_usage_table(path: Path | None = None) -> CodonUsageTable:
|
|
@@ -16,6 +16,8 @@ from factorforge import __version__
|
|
|
16
16
|
from factorforge.engines.registry import EngineRegistry
|
|
17
17
|
from factorforge.engines.profile.utils import parse_fasta_records
|
|
18
18
|
|
|
19
|
+
HOST_MAP = {"nbenthamiana": "nbenthamiana", "by2": "ntabacum"}
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
def _configure_stdio() -> None:
|
|
21
23
|
"""Best-effort UTF-8 for Windows consoles."""
|
|
@@ -79,6 +81,49 @@ def _format_dp_fasta(sequence_id: str, dna_sequence: str, cai: float, gc: float)
|
|
|
79
81
|
return f"{header}\n{_wrap_sequence(dna_sequence)}\n"
|
|
80
82
|
|
|
81
83
|
|
|
84
|
+
def _option_was_explicitly_set(option_name: str) -> bool:
|
|
85
|
+
"""Return whether an option was provided on the command line."""
|
|
86
|
+
ctx = click.get_current_context(silent=True)
|
|
87
|
+
if ctx is None:
|
|
88
|
+
return False
|
|
89
|
+
get_parameter_source = getattr(ctx, "get_parameter_source", None)
|
|
90
|
+
if not callable(get_parameter_source):
|
|
91
|
+
return False
|
|
92
|
+
return get_parameter_source(option_name) == click.core.ParameterSource.COMMANDLINE
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _engine_option_was_explicitly_set() -> bool:
|
|
96
|
+
"""Return whether --engine/-e was provided on the command line."""
|
|
97
|
+
return _option_was_explicitly_set("engine")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _format_profile_fasta(sequence_id: str, profile: str, result) -> str:
|
|
101
|
+
"""Format a profile optimization result as FASTA."""
|
|
102
|
+
cai = float(result.metrics.get("cai", 0.0))
|
|
103
|
+
gc = float(result.metrics.get("gc_percent", result.metrics.get("gc_content", 0.0)))
|
|
104
|
+
score = float(result.metrics.get("score", 0.0))
|
|
105
|
+
header = f">{sequence_id}|profile={profile}|cai={cai:.3f}|gc={gc:.2f}|score={score:.3f}"
|
|
106
|
+
return f"{header}\n{_wrap_sequence(result.sequence)}\n"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _format_profile_comparison_table(profile_results) -> str:
|
|
110
|
+
"""Format profile optimization metrics as a comparison table."""
|
|
111
|
+
divider = "─" * 45
|
|
112
|
+
lines = [
|
|
113
|
+
"Profile comparison results:",
|
|
114
|
+
divider,
|
|
115
|
+
f"{'Profile':<18}{'CAI':>7} {'GC%':>7} {'Score':>8}",
|
|
116
|
+
divider,
|
|
117
|
+
]
|
|
118
|
+
for profile_name, result in profile_results:
|
|
119
|
+
cai = float(result.metrics.get("cai", 0.0))
|
|
120
|
+
gc = float(result.metrics.get("gc_percent", result.metrics.get("gc_content", 0.0)))
|
|
121
|
+
score = float(result.metrics.get("score", 0.0))
|
|
122
|
+
lines.append(f"{profile_name:<18}{cai:>7.3f} {gc:>7.2f} {score:>8.3f}")
|
|
123
|
+
lines.append(divider)
|
|
124
|
+
return "\n".join(lines)
|
|
125
|
+
|
|
126
|
+
|
|
82
127
|
@click.group()
|
|
83
128
|
@click.version_option(version=__version__)
|
|
84
129
|
def cli():
|
|
@@ -106,6 +151,12 @@ def list_engines():
|
|
|
106
151
|
type=click.Choice(["dp", "profile"], case_sensitive=False),
|
|
107
152
|
help="Engine (dp, profile)",
|
|
108
153
|
)
|
|
154
|
+
@click.option(
|
|
155
|
+
"--host",
|
|
156
|
+
default="nbenthamiana",
|
|
157
|
+
type=click.Choice(["nbenthamiana", "by2"], case_sensitive=False),
|
|
158
|
+
help="Expression host: nbenthamiana (default) or by2 (Tobacco BY-2 / N. tabacum)",
|
|
159
|
+
)
|
|
109
160
|
@click.option("--profile", "-p", default="balanced", help="Optimization profile")
|
|
110
161
|
@click.option(
|
|
111
162
|
"--objective",
|
|
@@ -118,6 +169,13 @@ def list_engines():
|
|
|
118
169
|
@click.option("--template", "construct_template", help="Construct template name")
|
|
119
170
|
@click.option("--output", "-o", help="Output file")
|
|
120
171
|
@click.option("--format", "output_format", default="fasta", help="Output format (fasta, genbank)")
|
|
172
|
+
@click.option(
|
|
173
|
+
"--compare-profiles",
|
|
174
|
+
help=(
|
|
175
|
+
"Comma-separated profiles to compare "
|
|
176
|
+
"(e.g. balanced,high_cai,gc_target). Implies --engine profile."
|
|
177
|
+
),
|
|
178
|
+
)
|
|
121
179
|
@click.option(
|
|
122
180
|
"--scan-mode",
|
|
123
181
|
default="full",
|
|
@@ -129,6 +187,7 @@ def list_engines():
|
|
|
129
187
|
def optimize(
|
|
130
188
|
input_file,
|
|
131
189
|
engine,
|
|
190
|
+
host,
|
|
132
191
|
profile,
|
|
133
192
|
objective,
|
|
134
193
|
gc_min,
|
|
@@ -136,11 +195,28 @@ def optimize(
|
|
|
136
195
|
construct_template,
|
|
137
196
|
output,
|
|
138
197
|
output_format,
|
|
198
|
+
compare_profiles,
|
|
139
199
|
scan_mode,
|
|
140
200
|
scan_include,
|
|
141
201
|
scan_exclude,
|
|
142
202
|
):
|
|
143
203
|
"""Optimize protein sequence"""
|
|
204
|
+
compare_profile_list = _parse_csv_option(compare_profiles)
|
|
205
|
+
engine = engine.lower()
|
|
206
|
+
host_value = host.lower()
|
|
207
|
+
internal_host = HOST_MAP[host_value]
|
|
208
|
+
host_was_explicit = _option_was_explicitly_set("host")
|
|
209
|
+
|
|
210
|
+
if host_was_explicit and engine == "dp" and _engine_option_was_explicitly_set():
|
|
211
|
+
raise click.UsageError("--host is only supported with --engine profile")
|
|
212
|
+
if host_was_explicit and engine == "dp" and internal_host != "nbenthamiana":
|
|
213
|
+
engine = "profile"
|
|
214
|
+
|
|
215
|
+
if compare_profile_list:
|
|
216
|
+
if engine == "dp" and _engine_option_was_explicitly_set():
|
|
217
|
+
raise click.UsageError("--compare-profiles cannot be used with --engine dp.")
|
|
218
|
+
engine = "profile"
|
|
219
|
+
|
|
144
220
|
try:
|
|
145
221
|
# Read file
|
|
146
222
|
with open(input_file, encoding="utf-8") as f:
|
|
@@ -156,6 +232,38 @@ def optimize(
|
|
|
156
232
|
if len(fasta_records) == 1:
|
|
157
233
|
sequence = fasta_records[0][1]
|
|
158
234
|
|
|
235
|
+
if compare_profile_list:
|
|
236
|
+
if fasta_records is not None and len(fasta_records) > 1:
|
|
237
|
+
raise ValueError("Profile comparison requires a single input sequence.")
|
|
238
|
+
if construct_template:
|
|
239
|
+
raise ValueError("Profile comparison does not support --template mode.")
|
|
240
|
+
if output_format.lower() != "fasta":
|
|
241
|
+
raise ValueError("Profile comparison only supports FASTA output.")
|
|
242
|
+
|
|
243
|
+
optimizer = EngineRegistry.get("profile")
|
|
244
|
+
profile_results = []
|
|
245
|
+
for profile_name in compare_profile_list:
|
|
246
|
+
result = optimizer.optimize(
|
|
247
|
+
sequence,
|
|
248
|
+
profile=profile_name,
|
|
249
|
+
host=internal_host,
|
|
250
|
+
scan_mode=scan_mode,
|
|
251
|
+
scan_include=scan_include_list,
|
|
252
|
+
scan_exclude=scan_exclude_list,
|
|
253
|
+
)
|
|
254
|
+
profile_results.append((profile_name, result))
|
|
255
|
+
|
|
256
|
+
if output:
|
|
257
|
+
first_profile, first_result = profile_results[0]
|
|
258
|
+
sequence_id = Path(input_file).stem or "factorforge_profile"
|
|
259
|
+
fasta = _format_profile_fasta(sequence_id, first_profile, first_result)
|
|
260
|
+
with open(output, "w", encoding="utf-8") as f:
|
|
261
|
+
f.write(fasta)
|
|
262
|
+
click.echo(f"Saved to: {output}")
|
|
263
|
+
|
|
264
|
+
click.echo(_format_profile_comparison_table(profile_results))
|
|
265
|
+
return
|
|
266
|
+
|
|
159
267
|
if fasta_records is not None and len(fasta_records) > 1:
|
|
160
268
|
if engine == "dp":
|
|
161
269
|
raise ValueError("Multi-FASTA input requires --engine profile.")
|
|
@@ -170,6 +278,7 @@ def optimize(
|
|
|
170
278
|
results = optimizer.optimize_batch(
|
|
171
279
|
payload,
|
|
172
280
|
profile=profile,
|
|
281
|
+
host=internal_host,
|
|
173
282
|
scan_mode=scan_mode,
|
|
174
283
|
scan_include=scan_include_list,
|
|
175
284
|
scan_exclude=scan_exclude_list,
|
|
@@ -179,6 +288,7 @@ def optimize(
|
|
|
179
288
|
optimizer.optimize(
|
|
180
289
|
seq,
|
|
181
290
|
profile=profile,
|
|
291
|
+
host=internal_host,
|
|
182
292
|
scan_mode=scan_mode,
|
|
183
293
|
scan_include=scan_include_list,
|
|
184
294
|
scan_exclude=scan_exclude_list,
|
|
@@ -246,9 +356,14 @@ def optimize(
|
|
|
246
356
|
if engine == "profile" and construct_template:
|
|
247
357
|
from factorforge.engines.profile.pipeline import OptimizationPipeline
|
|
248
358
|
|
|
249
|
-
pipeline = OptimizationPipeline(
|
|
359
|
+
pipeline = OptimizationPipeline(
|
|
360
|
+
profile=profile,
|
|
361
|
+
construct_template=construct_template,
|
|
362
|
+
host=internal_host,
|
|
363
|
+
)
|
|
250
364
|
result = pipeline.run(
|
|
251
365
|
sequence,
|
|
366
|
+
host=internal_host,
|
|
252
367
|
scan_mode=scan_mode,
|
|
253
368
|
scan_include=scan_include_list,
|
|
254
369
|
scan_exclude=scan_exclude_list,
|
|
@@ -278,6 +393,7 @@ def optimize(
|
|
|
278
393
|
result = optimizer.optimize(
|
|
279
394
|
sequence,
|
|
280
395
|
profile=profile,
|
|
396
|
+
host=internal_host,
|
|
281
397
|
scan_mode=scan_mode,
|
|
282
398
|
scan_include=scan_include_list,
|
|
283
399
|
scan_exclude=scan_exclude_list,
|