factorforge-cds 3.2.7__tar.gz → 3.3.0__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.2.7/src/factorforge_cds.egg-info → factorforge_cds-3.3.0}/PKG-INFO +4 -4
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/README.md +2 -2
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/pyproject.toml +2 -2
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/__init__.py +1 -1
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/analysis/feasibility.py +6 -12
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/analysis/metrics.py +1 -1
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/__init__.py +1 -1
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/__init__.py +1 -1
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/optimizer.py +18 -2
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/reverse_translator.py +9 -6
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/rule_engine.py +8 -6
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring.py +15 -20
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/utils.py +4 -5
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0/src/factorforge_cds.egg-info}/PKG-INFO +4 -4
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_benchmark_codon_table_metadata.py +3 -6
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_registry_production_sync.py +19 -2
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_worked_example.py +9 -5
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/LICENSE +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/setup.cfg +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/__main__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/analysis/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/cli/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/cli/legacy_cli.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/cli/main.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/exporter.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/optimizer.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/validator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_codons.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_golden_set.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/ntabacum_codons.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/templates/high_expression.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/templates/standard_expression.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/wolffia_globosa_codons.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/database.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/codon_table_builder.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/construct_builder.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/exporter.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/pipeline.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/domesticator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring_ml.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/validator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/registry.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/io/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/io/fasta.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/io/validation.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/annotate.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/kd_scale.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/risk_classifier.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/sp_predict.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/tm_predict.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/registry/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/registry/registry_loader.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/schemas/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.schema.json +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/construct_id.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/exceptions.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/restriction_sites.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/sequence_validator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/utils/validation.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation/__init__.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation/cli.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation/package_generator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation_registry.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation_report.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/SOURCES.txt +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/dependency_links.txt +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/entry_points.txt +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/requires.txt +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/top_level.txt +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_baselines.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_benchmark_regression.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_benchmark_scoring.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_benchmark_smoke.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_cai.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_codon_table_manifest.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_database.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_design_package_schema.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_design_package_semantics.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_design_package_serialization.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_docs_consistency.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_fasta_io.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_gc_content.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_host_profile_metadata.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_iupac_validation.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_legacy_cli.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_no_raw_sequence_logging.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_openbio_missing_metric_contract.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_parameter_registry.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_protein_risk.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_restriction_sites.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_sequence_validator.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_translation_integrity.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_validation_contract_compat.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_validation_registry.py +0 -0
- {factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_validation_report.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
3
|
+
Version: 3.3.0
|
|
4
|
+
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review 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.eijex.com
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# FactorForge
|
|
35
35
|
|
|
36
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
36
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
37
37
|
|
|
38
38
|
[](LICENSE)
|
|
39
39
|
[](https://www.python.org/)
|
|
@@ -93,7 +93,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
93
93
|
## Citing
|
|
94
94
|
|
|
95
95
|
```
|
|
96
|
-
FactorForge v3.
|
|
96
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
97
97
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
98
98
|
```
|
|
99
99
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# FactorForge
|
|
2
2
|
|
|
3
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
3
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
4
4
|
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.python.org/)
|
|
@@ -60,7 +60,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
60
60
|
## Citing
|
|
61
61
|
|
|
62
62
|
```
|
|
63
|
-
FactorForge v3.
|
|
63
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
64
64
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "factorforge-cds"
|
|
7
|
-
version = "3.
|
|
8
|
-
description = "FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
7
|
+
version = "3.3.0"
|
|
8
|
+
description = "FactorForge - open-source CDS design and pre-synthesis sequence review by Eijex."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-only"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
@@ -14,13 +14,11 @@ from factorforge.analysis.metrics import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# DEFAULT_GC_LOW/HIGH
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# external ground truth ~44%). NOT an empirically validated expression optimum —
|
|
23
|
-
# this is a composition anchor, not a target to maximize toward.
|
|
17
|
+
# Defaults for the current N. benthamiana software-default codon reference.
|
|
18
|
+
# DEFAULT_CAI_TARGET=0.82 remains a soft in-silico target aligned with
|
|
19
|
+
# industry practice (>0.8). DEFAULT_GC_LOW/HIGH track the NbeV1.1
|
|
20
|
+
# high-confidence CDS-derived native composition band. These are not wet-lab
|
|
21
|
+
# validation, expression/yield prediction, or biological-superiority claims.
|
|
24
22
|
# Exported as named constants so tests/test_registry_production_sync.py can
|
|
25
23
|
# strictly compare them against the registry (single source of truth).
|
|
26
24
|
DEFAULT_CAI_TARGET: float = 0.82
|
|
@@ -120,11 +118,7 @@ def analyze_feasibility(
|
|
|
120
118
|
if not protein:
|
|
121
119
|
raise ValueError("protein_sequence must not be empty")
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
# progressively wider windows. (55.0, 65.0) is retained ONLY as an explicit
|
|
125
|
-
# non-native/high-GC option (matches legacy engine-output-calibrated band) —
|
|
126
|
-
# callers must opt in explicitly to it, it is not the production default.
|
|
127
|
-
ranges = gc_ranges or [(40.0, 47.0), (35.0, 50.0), (55.0, 65.0)]
|
|
121
|
+
ranges = gc_ranges or [(40.0, 47.0), (40.0, 55.0), (40.0, 65.0)]
|
|
128
122
|
normalized_ranges = [
|
|
129
123
|
(_normalize_gc_bound(low), _normalize_gc_bound(high)) for low, high in ranges
|
|
130
124
|
]
|
|
@@ -98,7 +98,7 @@ class CodonUsageTable:
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
def _default_codon_table_path() -> Path:
|
|
101
|
-
#
|
|
101
|
+
# v3.3.0 reference-policy update: production default switched from the
|
|
102
102
|
# legacy Kazusa/SGN-derived table to the NbeV1.1 LAB-strain high-confidence
|
|
103
103
|
# derived table. See data/reference/active_codon_reference.json.
|
|
104
104
|
return get_data_path() / "profiles" / "nbev11_cds_hc_derived_codons.json"
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/optimizer.py
RENAMED
|
@@ -10,7 +10,7 @@ from factorforge.core.interfaces import OptimizationResult, OptimizerEngine
|
|
|
10
10
|
from .exporter import SequenceExporter
|
|
11
11
|
from .rules.reverse_translator import OptimizationProfile, ReverseTranslator
|
|
12
12
|
from .rules.rule_engine import RuleEngine
|
|
13
|
-
from .scoring import calculate_composite_score, compute_mfe_evidence
|
|
13
|
+
from .scoring import calculate_composite_score, compute_mfe_evidence, resolve_host_gc_range
|
|
14
14
|
from .validator import InputValidator
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
@@ -20,7 +20,7 @@ class RuleBasedOptimizer(OptimizerEngine):
|
|
|
20
20
|
"""Profile-based rule optimization engine."""
|
|
21
21
|
|
|
22
22
|
name = "Profile-based"
|
|
23
|
-
version = "3.
|
|
23
|
+
version = "3.3.0"
|
|
24
24
|
|
|
25
25
|
def __init__(self, codon_table_path: str | None = None) -> None:
|
|
26
26
|
"""
|
|
@@ -157,6 +157,22 @@ class RuleBasedOptimizer(OptimizerEngine):
|
|
|
157
157
|
"score": candidates[0]["score"],
|
|
158
158
|
"violations": sum(len(v) for v in scan_results.values()),
|
|
159
159
|
}
|
|
160
|
+
if profile_value == "balanced":
|
|
161
|
+
host_gc_min, host_gc_max = resolve_host_gc_range(host)
|
|
162
|
+
requested_gc_min_percent = float(kwargs.get("target_gc_min", host_gc_min))
|
|
163
|
+
requested_gc_max_percent = float(kwargs.get("target_gc_max", host_gc_max))
|
|
164
|
+
achieved_gc_percent = metrics["gc_percent"]
|
|
165
|
+
metrics.update(
|
|
166
|
+
{
|
|
167
|
+
"gc_target_reached": (
|
|
168
|
+
requested_gc_min_percent
|
|
169
|
+
<= achieved_gc_percent
|
|
170
|
+
<= requested_gc_max_percent
|
|
171
|
+
),
|
|
172
|
+
"requested_gc_min_percent": requested_gc_min_percent,
|
|
173
|
+
"requested_gc_max_percent": requested_gc_max_percent,
|
|
174
|
+
}
|
|
175
|
+
)
|
|
160
176
|
# MFE provenance: expose whether MFE was actually computed so downstream
|
|
161
177
|
# artifacts (API response, Design Package) never report an uncomputed
|
|
162
178
|
# MFE as a misleading 0.0 (016 audit). Score value is unchanged.
|
|
@@ -374,11 +374,14 @@ class ReverseTranslator:
|
|
|
374
374
|
Balanced profile: CAI first, GC balanced
|
|
375
375
|
|
|
376
376
|
- Preferred codon ratio: 70%
|
|
377
|
-
- Target GC: host composition band
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
377
|
+
- Target GC: host composition band, resolved via
|
|
378
|
+
resolve_host_gc_range() (engines/profile/scoring.py
|
|
379
|
+
GC_OPT_MIN/MAX). N. benthamiana currently uses the NbeV1.1
|
|
380
|
+
high-confidence CDS-derived native-composition band (40-47%);
|
|
381
|
+
other hosts retain their configured defaults.
|
|
382
|
+
- GC target requests are best-effort sampling targets, not guarantees:
|
|
383
|
+
achieved GC can be dominated by the codon table's frequency-weighted
|
|
384
|
+
sampling distribution.
|
|
382
385
|
"""
|
|
383
386
|
_host_gc_min, _host_gc_max = resolve_host_gc_range(self.host)
|
|
384
387
|
target_gc_min = kwargs.get("target_gc_min", _host_gc_min)
|
|
@@ -453,7 +456,7 @@ class ReverseTranslator:
|
|
|
453
456
|
|
|
454
457
|
Targets the caller-supplied ``target_gc`` if provided, otherwise the
|
|
455
458
|
active host's composition midpoint (resolve_host_gc_range(self.host);
|
|
456
|
-
43.5% for nbenthamiana since v3.3.0 /
|
|
459
|
+
43.5% for nbenthamiana since v3.3.0 / reference-policy audit, 60.0% for other
|
|
457
460
|
hosts pending their own host-specific analysis). To target a different
|
|
458
461
|
GC (e.g. for specific vector requirements), pass target_gc explicitly.
|
|
459
462
|
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/rule_engine.py
RENAMED
|
@@ -358,12 +358,14 @@ class RuleEngine:
|
|
|
358
358
|
|
|
359
359
|
This is a LOCAL synthesis/extreme-window guard (default 25-75% over a
|
|
360
360
|
50 bp window), NOT the global GC target. Global GC is governed separately
|
|
361
|
-
by the scoring band (GC_OPT_MIN/MAX
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
361
|
+
by the scoring band (GC_OPT_MIN/MAX; N. benthamiana currently uses
|
|
362
|
+
the NbeV1.1 high-confidence CDS-derived native-composition anchor,
|
|
363
|
+
40-47%, while other hosts retain their own configured defaults) and
|
|
364
|
+
the API/DP gc_min/gc_max constraints. The wide 25-75% local band is
|
|
365
|
+
an independent synthesis/hairpin-risk guard
|
|
366
|
+
(registry `gc_extreme_local_window`), not derived from or coupled to
|
|
367
|
+
the global GC target — narrowing it is a separate decision from the
|
|
368
|
+
global band retarget and is out of scope here.
|
|
367
369
|
|
|
368
370
|
Args:
|
|
369
371
|
seq: DNA sequence
|
|
@@ -11,23 +11,20 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
|
-
# GC band for N. benthamiana
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# GC_OPT_MAX] receive full GC score; outside the band the score decays linearly.
|
|
14
|
+
# GC band for the current N. benthamiana software-default codon reference.
|
|
15
|
+
# NbeV1.1 high-confidence CDS-derived codon usage anchors the public default
|
|
16
|
+
# to the native genome-derived composition band. This is a configured
|
|
17
|
+
# in-silico reference/GC policy, not wet-lab validation or expression/yield
|
|
18
|
+
# prediction. Sequences inside [GC_OPT_MIN, GC_OPT_MAX] receive full GC score;
|
|
19
|
+
# outside the band the score decays linearly.
|
|
21
20
|
GC_OPT_MIN = 40.0
|
|
22
21
|
GC_OPT_MAX = 47.0
|
|
23
22
|
GC_OPT_MID = 43.5 # kept for gc_target point-scoring and viral_delivery centering
|
|
24
23
|
GC_DECAY_WIDTH = 20.0 # percentage points outside band before score reaches 0.0
|
|
25
24
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# their own host-specific genome-composition analysis — they must NOT silently
|
|
30
|
-
# inherit GC_OPT_MIN/MAX, which is an N.-benthamiana-specific anchor.
|
|
25
|
+
# Host-isolation fix: ntabacum/BY-2 must not silently inherit the NbeV1.1
|
|
26
|
+
# N. benthamiana band. Other hosts keep the pre-v3.3.0 global default until
|
|
27
|
+
# they get their own host-specific analysis.
|
|
31
28
|
GC_RANGE_DEFAULT: tuple[float, float] = (55.0, 65.0)
|
|
32
29
|
GC_RANGES_BY_HOST: dict[str, tuple[float, float]] = {
|
|
33
30
|
"nbenthamiana": (GC_OPT_MIN, GC_OPT_MAX),
|
|
@@ -37,7 +34,7 @@ GC_RANGES_BY_HOST: dict[str, tuple[float, float]] = {
|
|
|
37
34
|
def resolve_host_gc_range(host: str | None) -> tuple[float, float]:
|
|
38
35
|
"""Resolve the (gc_min, gc_max) composition band for a host.
|
|
39
36
|
|
|
40
|
-
nbenthamiana uses the
|
|
37
|
+
nbenthamiana uses the reference-policy audit native genome-composition anchor.
|
|
41
38
|
Any other host (including unknown ones) keeps the pre-v3.3.0 global
|
|
42
39
|
default band until it gets its own host-specific analysis.
|
|
43
40
|
"""
|
|
@@ -142,7 +139,7 @@ def _check_vienna_available() -> bool:
|
|
|
142
139
|
return _vienna_available
|
|
143
140
|
|
|
144
141
|
|
|
145
|
-
#
|
|
142
|
+
# ViennaRNA's RNA.fold() uses Zuker's MFE algorithm, O(n^3) time /
|
|
146
143
|
# O(n^2) memory — there was previously no length guard anywhere in the
|
|
147
144
|
# calculate_mfe() call chain, so a single request at/under the existing
|
|
148
145
|
# public API length limits (5000aa/15000bp) could pin a CPU core for minutes
|
|
@@ -175,7 +172,7 @@ def calculate_mfe(sequence: str) -> float | None:
|
|
|
175
172
|
"Sequence length (%d nt) exceeds MFE_MAX_SEQUENCE_LENGTH (%d nt); "
|
|
176
173
|
"skipping global MFE calculation to avoid an unbounded ViennaRNA "
|
|
177
174
|
"RNA.fold() runtime (O(n^3)). MFE scoring falls back to a neutral, "
|
|
178
|
-
"zero-weighted contribution for this candidate
|
|
175
|
+
"zero-weighted contribution for this candidate.",
|
|
179
176
|
len(sequence), MFE_MAX_SEQUENCE_LENGTH,
|
|
180
177
|
)
|
|
181
178
|
return None
|
|
@@ -196,8 +193,7 @@ def normalize_mfe(mfe: float, seq_length: int) -> float:
|
|
|
196
193
|
"""
|
|
197
194
|
Normalize MFE to 0-1 range where 1 = no structure (favorable).
|
|
198
195
|
|
|
199
|
-
Clamp range calibrated empirically
|
|
200
|
-
(eijex-workspace/_analysis/2026-06-26/011-mfe-clamp-calibration):
|
|
196
|
+
Clamp range calibrated empirically from internal computational audit data:
|
|
201
197
|
measured MFE/nt across 135 FactorForge outputs (N. benthamiana +
|
|
202
198
|
BY-2 hosts, 5 profiles) ranged -0.4064 to -0.1338 (combined 5th/95th
|
|
203
199
|
percentile -0.3839/-0.1760). The range below widens that empirical
|
|
@@ -211,8 +207,7 @@ def normalize_mfe(mfe: float, seq_length: int) -> float:
|
|
|
211
207
|
Returns:
|
|
212
208
|
Normalized MFE score (0-1) under this computational normalization,
|
|
213
209
|
where a higher value represents a less negative whole-CDS MFE/nt.
|
|
214
|
-
This is a Tier-0 computational heuristic only
|
|
215
|
-
eijex-validationHub/docs/CLAIM_EVIDENCE_BENCHMARK_MODEL.md §4) — no
|
|
210
|
+
This is a Tier-0 computational heuristic only — no
|
|
216
211
|
biological interpretation (translation efficiency, mRNA stability,
|
|
217
212
|
or expression outcome) is implied or validated.
|
|
218
213
|
"""
|
|
@@ -418,7 +413,7 @@ def compute_mfe_evidence(
|
|
|
418
413
|
elif not _check_vienna_available():
|
|
419
414
|
reason = "MFE was not computed because ViennaRNA is unavailable in this environment."
|
|
420
415
|
elif len(sequence) > MFE_MAX_SEQUENCE_LENGTH:
|
|
421
|
-
#
|
|
416
|
+
# Distinguish a deliberate length-based skip from an actual
|
|
422
417
|
# fold failure — the generic "computation failed" message below would
|
|
423
418
|
# otherwise mislead a caller into thinking something is broken.
|
|
424
419
|
reason = (
|
|
@@ -115,11 +115,10 @@ def calculate_dinucleotide_ratio(sequence: str, dinucleotide: str = "CG") -> flo
|
|
|
115
115
|
return observed / expected
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
# historical benchmark replay (benchmarks/run_benchmark.py --codon-table-path).
|
|
118
|
+
# NbeV1.1 high-confidence CDS-derived codon usage is the production-default
|
|
119
|
+
# N. benthamiana software reference. This is a provenance-tracked in-silico
|
|
120
|
+
# default, not wet-lab validation or a biological-superiority claim; see
|
|
121
|
+
# data/reference/active_codon_reference.json.
|
|
123
122
|
_HOST_CODON_TABLE_OVERRIDES: dict[str, str] = {
|
|
124
123
|
"nbenthamiana": "profiles/nbev11_cds_hc_derived_codons.json",
|
|
125
124
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: factorforge-cds
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review
|
|
3
|
+
Version: 3.3.0
|
|
4
|
+
Summary: FactorForge - open-source CDS design and pre-synthesis sequence review 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.eijex.com
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
|
|
34
34
|
# FactorForge
|
|
35
35
|
|
|
36
|
-
**Open-source constraint-based CDS design and pre-synthesis sequence review
|
|
36
|
+
**Open-source constraint-based CDS design and pre-synthesis sequence review for plant CDS workflows, with primary support for *Nicotiana benthamiana* (Tobacco BY-2: experimental).**
|
|
37
37
|
|
|
38
38
|
[](LICENSE)
|
|
39
39
|
[](https://www.python.org/)
|
|
@@ -93,7 +93,7 @@ FactorForge outputs are **in-silico only** and have not been experimentally vali
|
|
|
93
93
|
## Citing
|
|
94
94
|
|
|
95
95
|
```
|
|
96
|
-
FactorForge v3.
|
|
96
|
+
FactorForge v3.3.0 (2026). Open-source constraint-based CDS design and sequence review.
|
|
97
97
|
Eijex. https://github.com/eijex/factorforge-cds
|
|
98
98
|
```
|
|
99
99
|
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_benchmark_codon_table_metadata.py
RENAMED
|
@@ -111,8 +111,8 @@ def test_smoke_summary_contains_codon_table_fields(tmp_path):
|
|
|
111
111
|
assert field in data, f"Missing codon table field in smoke summary JSON: {field}"
|
|
112
112
|
|
|
113
113
|
# No --codon-table-path override ⇒ codon_table_id must reflect whichever
|
|
114
|
-
# asset is actually the current production default (
|
|
115
|
-
#
|
|
114
|
+
# asset is actually the current production default (v3.3.0 reference-policy update,
|
|
115
|
+
# reference-policy audit — see data/reference/active_codon_reference.json), not a
|
|
116
116
|
# hardcoded legacy literal.
|
|
117
117
|
active_ref = json.loads(
|
|
118
118
|
(ROOT / "data" / "reference" / "active_codon_reference.json").read_text(encoding="utf-8")
|
|
@@ -148,9 +148,6 @@ def test_smoke_summary_contains_vienna_rna_active(tmp_path):
|
|
|
148
148
|
|
|
149
149
|
def test_no_raw_genome_fasta_committed():
|
|
150
150
|
"""Raw genome/CDS/protein FASTA files must not be committed to the repo."""
|
|
151
|
-
forbidden_patterns = ["*.fasta", "*.fa", "*.fna", "*.fastq"]
|
|
152
|
-
allowed_fixtures = ROOT / "tests" / "fixtures"
|
|
153
|
-
|
|
154
151
|
import subprocess
|
|
155
152
|
result = subprocess.run(
|
|
156
153
|
["git", "ls-files", "--", "*.fasta", "*.fa", "*.fna", "*.fastq"],
|
|
@@ -168,6 +165,6 @@ def test_no_raw_genome_fasta_committed():
|
|
|
168
165
|
if p and not any(p.startswith(pfx) for pfx in allowed_prefixes)
|
|
169
166
|
]
|
|
170
167
|
assert not committed, (
|
|
171
|
-
|
|
168
|
+
"Raw FASTA/genome files must not be committed outside allowed locations:\n"
|
|
172
169
|
+ "\n".join(committed)
|
|
173
170
|
)
|
|
@@ -76,7 +76,7 @@ def test_aa_identity_policy_sync():
|
|
|
76
76
|
assert ok["passed"] and ok["aa_identity"] == registry_val
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
# ── codon_reference source-of-truth sync (
|
|
79
|
+
# ── codon_reference source-of-truth sync (v3.3.0 reference-policy update) ────
|
|
80
80
|
|
|
81
81
|
def test_codon_reference_active_sync_with_active_reference_file():
|
|
82
82
|
"""registry's codon_reference.active block must match
|
|
@@ -105,7 +105,7 @@ def test_codon_reference_active_sync_with_active_reference_file():
|
|
|
105
105
|
|
|
106
106
|
def test_codon_reference_active_sync_with_v2_manifest():
|
|
107
107
|
"""registry's codon_reference.active block must match the schema-conformant
|
|
108
|
-
v2 manifest file's facts
|
|
108
|
+
NbeV1.1 HC (v2) manifest file's facts now that v2 is the production default."""
|
|
109
109
|
import json
|
|
110
110
|
from pathlib import Path
|
|
111
111
|
|
|
@@ -120,6 +120,23 @@ def test_codon_reference_active_sync_with_v2_manifest():
|
|
|
120
120
|
assert registry_active["source_status"] == manifest["source_status"]
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
def test_codon_reference_historical_comparator_sync_with_legacy_manifest():
|
|
124
|
+
"""registry's codon_reference.historical_comparator block must stay in sync
|
|
125
|
+
with the legacy v1 manifest retained for v3.2.x continuity/replay."""
|
|
126
|
+
import json
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
|
|
129
|
+
registry_historical = _resolve("codon_reference.historical_comparator")
|
|
130
|
+
manifest = json.loads(
|
|
131
|
+
(Path(__file__).resolve().parents[1] / "data" / "reference"
|
|
132
|
+
/ "codon_table_manifest.json").read_text(encoding="utf-8")
|
|
133
|
+
)
|
|
134
|
+
assert registry_historical["id"] == manifest["codon_table_id"]
|
|
135
|
+
assert registry_historical["sha256"] == manifest["sha256"]
|
|
136
|
+
assert registry_historical["asset_type"] == manifest["asset_type"]
|
|
137
|
+
assert registry_historical["source_status"] == manifest["source_status"]
|
|
138
|
+
|
|
139
|
+
|
|
123
140
|
def test_codon_reference_active_table_sha256_matches_production_default():
|
|
124
141
|
"""The sha256 recorded for the active codon_reference must match the
|
|
125
142
|
actual file the production engine resolves to by default."""
|
|
@@ -63,7 +63,7 @@ def test_sfgfp_sequence_no_x_residues():
|
|
|
63
63
|
def test_run_example_deterministic():
|
|
64
64
|
"""run_example.py (no --freeze) must exit 0 when frozen outputs exist.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
v3.3.0 reference-policy update: this script is explicitly pinned to the
|
|
67
67
|
legacy v1 codon reference (GC 55-65%) regardless of the engine's current
|
|
68
68
|
production default, so this reproducibility check stays valid forever.
|
|
69
69
|
See run_example_v2_smoke.py / test_run_example_v2_smoke_succeeds for the
|
|
@@ -82,10 +82,13 @@ def test_run_example_deterministic():
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def test_run_example_v2_smoke_succeeds():
|
|
85
|
-
"""run_example_v2_smoke.py (
|
|
85
|
+
"""run_example_v2_smoke.py (tracks whatever the current production
|
|
86
|
+
default actually is) must exit 0 and report a provenance ID that matches
|
|
87
|
+
data/reference/active_codon_reference.json — not hardcoded to v2, since
|
|
88
|
+
the example is aligned to the current v3.3.0 production-default reference policy.
|
|
86
89
|
|
|
87
|
-
No frozen-output comparison — this only checks that the
|
|
88
|
-
end-to-end and reports correct provenance
|
|
90
|
+
No frozen-output comparison — this only checks that the current-default
|
|
91
|
+
path runs end-to-end and reports correct provenance.
|
|
89
92
|
"""
|
|
90
93
|
result = subprocess.run(
|
|
91
94
|
[sys.executable, str(EXAMPLE_DIR / "run_example_v2_smoke.py")],
|
|
@@ -98,7 +101,8 @@ def test_run_example_v2_smoke_succeeds():
|
|
|
98
101
|
f"stdout:{result.stdout}\nstderr:{result.stderr}"
|
|
99
102
|
)
|
|
100
103
|
assert "OK" in result.stdout
|
|
101
|
-
|
|
104
|
+
active_ref = _load(ROOT / "data" / "reference" / "active_codon_reference.json")
|
|
105
|
+
assert active_ref["active_codon_table_id"] in result.stdout
|
|
102
106
|
|
|
103
107
|
|
|
104
108
|
# ---------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/optimizer.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/core/interfaces/validator.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_codons.json
RENAMED
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/nbenthamiana_golden_set.json
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/templates/high_expression.json
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/data/wolffia_globosa_codons.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/construct_builder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/rules/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/scoring_ml.py
RENAMED
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/engines/profile/validator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/protein_risk/risk_classifier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/schemas/design_package.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge/validation/package_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/src/factorforge_cds.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{factorforge_cds-3.2.7 → factorforge_cds-3.3.0}/tests/test_openbio_missing_metric_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|