API reference¶
Region analysis¶
Analyze a protein region for aggregation-prone clusters.
This is the main analysis function that orchestrates the entire aggregation detection pipeline: 1. Apply all (or selected) rules to the region 2. Collect identified clusters 3. Merge overlapping clusters using Union-Find 4. Identify multi-rule convergence hotspots 5. Build comprehensive results structure
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Full protein sequence (1-letter codes) |
required |
start
|
int
|
Region start position (1-indexed, inclusive) |
required |
stop
|
int
|
Region end position (1-indexed, inclusive) |
required |
registry
|
RuleRegistry
|
RuleRegistry with evaluators to use. If None, uses DEFAULT_REGISTRY. |
None
|
selected_rules
|
Optional[List[str]]
|
Optional list of rule names to apply. If None, all registered rules are used. |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with comprehensive analysis results: |
Dict[str, Any]
|
{ 'region': (start, stop), 'sequence': region_sequence, 'length': region_length, 'rules': {rule_name: rule_results, ...}, 'merged_clusters': [cluster_dicts, ...], 'multi_rule_clusters': [cluster_dicts, ...], 'aggregation_hotspots': [positions, ...], 'all_cluster_positions': [positions, ...] |
Dict[str, Any]
|
} |
Example
seq = "ALVIFQNWYG" results = analyze_region(seq, 1, 10) results['region'] (1, 10) len(results['merged_clusters']) 2
Source code in src/aggressor/analysis/regions.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
Mutation engine¶
Generate mutated sequences with point mutations, rule-based mutations, and insertions.
Mutation types generated: - Direct point mutations at specified positions - Rule-based mutations in aggregation-prone regions - Insertions at specified positions - Edge positions receive additional gatekeeping mutations
All mutations are classified by structural context: BETA_CORE, GATEKEEPER, BOUNDARY, FLANKING, DIRECT, INSERTION
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Original protein sequence |
required |
positions
|
List[int]
|
List of 1-indexed positions for direct mutations |
required |
mutations
|
List[str]
|
Amino acid codes to mutate to |
required |
regions
|
List[str]
|
List of region strings (e.g., ["10:50", "60:100"]) |
None
|
selected_rules
|
List[str]
|
Optional subset of rules to apply |
None
|
insertion_positions
|
List[int]
|
Positions for insertions (before this position) |
None
|
insertion_aas
|
List[str]
|
Amino acids to insert |
None
|
gatekeeping_aas
|
List[str]
|
Gatekeeping amino acids for edge positions |
None
|
verbose
|
bool
|
Enable debug logging |
False
|
Returns:
| Type | Description |
|---|---|
List[Tuple[str, str]]
|
Tuple of: |
List[Dict]
|
|
Tuple[List[Tuple[str, str]], List[Dict]]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If positions are out of range or insertion args mismatch |
Source code in src/aggressor/mutagenesis/engine.py
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 | |
Apply mutations based on aggregation rules with biological classification.
Algorithm: 1. Filter rules if specific ones selected 2. Collect all qualifying clusters 3. Merge overlapping clusters 4. For each position in merged clusters: - Determine if edge position - Apply standard + gatekeeping mutations as appropriate - Classify mutation type (GATEKEEPER, BETA_CORE, BOUNDARY, FLANKING)
Gatekeeper placement
By default every APR boundary position receives the gatekeeping
amino acids in addition to the standard mutation set. This reflects
the biological reality that the number of effective gatekeeper slots
is a structural property of the APR (its two flanks), not a free
parameter. When max_gatekeepers_per_apr is set, the boundary
slots are ranked by predicted aggregation-propensity reduction
(Tartaglia scale) and only the top-N are gatekept — an experimental
design budget control, not a change to the underlying biology.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
str
|
Full protein sequence |
required |
region_analysis
|
Dict
|
Analysis results from analyze_region() |
required |
mutations
|
List[str]
|
Standard mutation amino acids |
required |
selected_rules
|
List[str]
|
Optional subset of rules to apply |
None
|
gatekeeping_aas
|
List[str]
|
Gatekeeping amino acids for edge positions |
None
|
gatekeeper_distance
|
int
|
Distance threshold for gatekeeper classification |
GATEKEEPER_DISTANCE
|
max_gatekeepers_per_apr
|
Optional[int]
|
Optional cap on gatekept boundary slots per APR |
None
|
Returns:
| Type | Description |
|---|---|
List[Tuple[str, str, int, MutationType]]
|
List of (description, sequence, agg_score, mutation_type) tuples |
Source code in src/aggressor/mutagenesis/engine.py
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
Gatekeepers¶
Gatekeeper-slot detection and optimal selection.
Biological rationale¶
Aggregation-prone regions (APRs) form the cross-beta core of amyloid fibrils. Natural proteins suppress this by enriching gatekeeper residues at the APR flanks (Rousseau et al., J Mol Biol 2006; Beerten et al., FEBS Lett 2012; Reumers/Maurer-Stroh and colleagues on sequence-encoded aggregation safeguards). Two mechanisms dominate:
- Charged residues (D, E, K, R) impose electrostatic repulsion between molecules stacking in-register in the fibril, and raise local solubility.
- Proline is a beta-strand breaker: its backbone cannot donate an amide hydrogen bond and its phi is conformationally restricted, so it truncates beta-sheet propagation.
A key consequence for design: the number of effective gatekeeper slots is a structural property of the APR — essentially its two boundaries (and the immediately flanking positions) — not a free integer the user picks. This module therefore makes the automatic choice the primary mode:
- Detect whether a gatekeeper substitution at a boundary is even useful (skip positions already occupied by a gatekeeper residue — "is it possible / needed").
- When the caller supplies a budget cap, rank candidate slots by the predicted reduction in intrinsic aggregation propensity (Tartaglia et al., J Mol Biol 2008 scale) and keep the highest-value ones.
Public API¶
select_gatekeeper_positions(...) -> Set[int] # boundary slots to gatekeep flanking_gatekeeper_positions(...) -> List[int] # conservative out-of-APR flanks rank_gatekeeper_slots(...) -> List[(pos, gain)] best_gatekeeper_for_position(...) -> Optional[str]
best_gatekeeper_for_position(position, sequence, gatekeeping_aas)
¶
Choose the single most propensity-lowering gatekeeper AA for a position.
Returns the gatekeeper amino acid that yields the lowest intrinsic aggregation propensity at this site, or None if the original residue is already a gatekeeper (substitution would not help).
Example
best_gatekeeper_for_position(1, "I", {"P", "K", "R", "D", "E"}) 'K'
Source code in src/aggressor/analysis/gatekeepers.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | |
rank_gatekeeper_slots(candidate_positions, sequence, gatekeeping_aas)
¶
Rank candidate gatekeeper positions by predicted propensity reduction.
The "gain" at a position is (propensity of the original residue) minus (propensity of the best gatekeeper substitution). High-propensity boundary residues (I, V, L, F) yield the largest gains and are therefore the most valuable gatekeeper slots.
Positions whose residue is already a gatekeeper are excluded.
Returns:
| Type | Description |
|---|---|
List[Tuple[int, float]]
|
List of (position, gain) sorted by gain descending, then position |
List[Tuple[int, float]]
|
ascending (deterministic tie-break). |
Source code in src/aggressor/analysis/gatekeepers.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
select_gatekeeper_positions(cluster_positions, sequence, gatekeeping_aas=CANONICAL_GATEKEEPER_AAS, max_gatekeepers=None)
¶
Decide which APR boundary positions should receive gatekeeping AAs.
Default (max_gatekeepers is None): both cluster boundaries — i.e.
{min, max} — exactly the automatic behaviour. This is intentional: the
two flanks are the biologically meaningful gatekeeper slots, so the
count is detected from APR geometry, not requested by the user.
With a cap: the boundary slots are ranked by predicted propensity
reduction and only the top max_gatekeepers are returned. Slots whose
residue is already a gatekeeper are skipped, so the cap is applied to
useful slots only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cluster_positions
|
List[int]
|
All positions in the merged APR cluster. |
required |
sequence
|
str
|
Full protein sequence (for residue lookup). |
required |
gatekeeping_aas
|
Iterable[str]
|
Allowed gatekeeper amino acids. |
CANONICAL_GATEKEEPER_AAS
|
max_gatekeepers
|
Optional[int]
|
Optional budget cap on gatekept slots per APR. |
None
|
Returns:
| Type | Description |
|---|---|
Set[int]
|
Set of 1-indexed positions that should receive gatekeeping AAs. |
Example
sorted(select_gatekeeper_positions([5, 6, 7], "AAAAIFLAA")) [5, 7]
Source code in src/aggressor/analysis/gatekeepers.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
flanking_gatekeeper_positions(cluster_positions, region_start, region_end)
¶
Return the positions immediately flanking an APR (one N-terminal, one C-terminal), clamped to the analysis region.
This supports the conservative gatekeeper-design strategy in which a charge is introduced just outside the APR core, leaving the cross-beta register intact while walling it off electrostatically — distinct from substituting a terminal core residue. Exposed for downstream/expert use; not part of the default mutation path so that standard output stays backward-compatible.
Source code in src/aggressor/analysis/gatekeepers.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
Data models¶
Bases: Enum
Classification of mutations by structural/functional context.
Based on the gatekeeper hypothesis (Rousseau et al., 2006) and aggregation-prone region (APR) architecture.
Categories
BETA_CORE: Within identified aggregation cluster (highest risk) GATEKEEPER: At cluster boundary with gatekeeper AA (disrupts β-extension) FLANKING: Adjacent to APR but not gatekeeper position/AA BOUNDARY: At APR boundary but mutation is not to gatekeeper AA DIRECT: User-specified position (no rule context) INSERTION: Amino acid insertion IDR_PROXIMAL: Near intrinsically disordered region (future use) UNKNOWN: Cannot classify position/mutation
Source code in src/aggressor/core/models.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
from_description(description)
classmethod
¶
Parse mutation type from a description string.
Uses keyword matching to determine the mutation type from the formatted description strings generated by the pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
description
|
str
|
Formatted mutation description string |
required |
Returns:
| Type | Description |
|---|---|
MutationType
|
Corresponding MutationType enum value |
Examples:
>>> MutationType.from_description("A10P | GATEKEEPER (agg_score=3)")
<MutationType.GATEKEEPER: 2>
Source code in src/aggressor/core/models.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
__str__()
¶
Human-readable string representation.
Source code in src/aggressor/core/models.py
89 90 91 | |
Immutable representation of an aggregation-prone cluster identified by a single rule.
Using frozen=True enables: - Hashability (can be used in sets, as dict keys) - Thread safety (no mutation possible after creation) - Semantic clarity (clusters are identified, not modified)
Using slots=True reduces memory footprint by ~40% per instance compared to standard dataclass with dict.
Attributes:
| Name | Type | Description |
|---|---|---|
positions |
Tuple[int, ...]
|
1-indexed positions of residues in the cluster (must be sorted) |
residues |
Tuple[str, ...]
|
Amino acid single-letter codes at each position |
rule_name |
str
|
Name of the rule that identified this cluster |
aggregation_score |
int
|
Integer score indicating aggregation propensity weight |
Raises:
| Type | Description |
|---|---|
ValueError
|
If positions and residues have different lengths, if positions are empty, or if positions are not in sorted order |
Source code in src/aggressor/core/models.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | |
size
property
¶
Number of residues in the cluster.
span
property
¶
Sequence span from first to last position (inclusive).
density
property
¶
Cluster density: occupied positions / total span.
Higher density indicates tighter packing of aggregation-prone residues in the primary sequence, which correlates with stronger aggregation propensity.
motif
property
¶
The cluster residues concatenated as a sequence motif.
mean_propensity
property
¶
Average intrinsic aggregation propensity of cluster residues.
Uses the Tartaglia et al. (2008) aggregation propensity scale. Higher values indicate stronger predicted aggregation tendency.
min_position
property
¶
N-terminal boundary of the cluster.
max_position
property
¶
C-terminal boundary of the cluster.
__post_init__()
¶
Validate cluster invariants after initialization.
Source code in src/aggressor/core/models.py
188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
overlaps_with(other, gap_tolerance=0)
¶
Check if this cluster overlaps with another cluster.
Two clusters overlap if their position ranges intersect or are within gap_tolerance positions of each other.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Cluster
|
Another cluster to check for overlap |
required |
gap_tolerance
|
int
|
Maximum allowed gap between clusters to still consider them overlapping |
0
|
Returns:
| Type | Description |
|---|---|
bool
|
True if clusters overlap or are within gap_tolerance |
Examples:
>>> c1 = Cluster((1,2,3), ('A','L','V'), 'test', 1)
>>> c2 = Cluster((4,5), ('I','F'), 'test', 1)
>>> c1.overlaps_with(c2, gap_tolerance=1)
True
>>> c1.overlaps_with(c2, gap_tolerance=0)
False
Source code in src/aggressor/core/models.py
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | |
to_dict()
¶
Convert to dictionary for serialization and backward compatibility.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with all cluster properties |
Source code in src/aggressor/core/models.py
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | |