-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenmethod.py
More file actions
99 lines (77 loc) · 3.91 KB
/
Copy pathgenmethod.py
File metadata and controls
99 lines (77 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
92
93
94
95
96
97
98
99
# === genmethod.py ===
"""
GenMethod: abstract base class for sequence generation methods.
A GenMethod produces one candidate sequence string per call. Concrete methods
(Swap, Hybrid, FacesMix, Pattern, Flanks, etc.) subclass GenMethod and
implement generate(). The Generator owns one or more GenMethod instances,
selects among them, and validates whatever string they return.
Contract for subclasses:
- generate(seq1, seq2, verbose) returns a plain amino-acid string. Validation
against restrictions is the Generator's job, not the method's.
- Two parents are always passed in by the Generator, even when a method uses
fewer. `expected_parents` declares how many a method actually needs, so the
Generator can filter out methods it cannot satisfy with the parents at hand
(e.g. a 2-parent method when only one individual is available).
- Shared resources (amino acid pool, target length) are reached through
self.generator, a back-reference the Generator injects on registration.
Methods that do not need them can ignore it.
Subclasses needing physicochemical properties should read them straight from
the Sequence objects (seq.residues, seq.charge, ...). Those needing only the
letters should use str(seq) to avoid building extra objects.
"""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from generator import Generator
from sequence import Sequence
logger = logging.getLogger(__name__)
class GenMethod:
"""
Abstract base class for sequence generation methods.
Subclasses implement generate() to produce a new candidate sequence string
from two parent Sequence objects. The parents are always provided by Evolver
via Generator, even when a method does not use both.
Access to shared resources (amino acid pool, peptide length) is provided
through a reference to the parent Generator, injected automatically when the
method is registered. Methods that do not need these resources can ignore
the reference entirely.
Subclasses that require physicochemical properties of the parents should
access them directly from the Sequence objects (e.g. seq.residues,
seq.charge). Subclasses that only need the sequence string should use
str(seq) to avoid unnecessary object construction overhead.
Class attributes:
method_name (str): Short label used in logs and verbose output.
expected_parents (int): Number of parents the method requires; the
Generator uses it to filter methods when fewer parents are available.
"""
method_name: str = 'EmptyMethod'
expected_parents: int = 2
def __init__(self) -> None:
# Back-reference to the owning Generator, injected by
# Generator._register_method(). Provides access to aa_pool,
# peptide_len, and other shared state. None until registered.
self.generator: Generator | None = None
def generate(self, seq1: Sequence, seq2: Sequence, verbose=False) -> str:
"""
Produce a candidate sequence string from two parent Sequence objects.
Both parents are always provided. Methods that use only one parent may
ignore seq2; methods that build from scratch may ignore both. Must
return a plain string of amino-acid single-letter codes; validation is
handled by the Generator after this method returns.
Args:
seq1 (Sequence): First parent.
seq2 (Sequence): Second parent.
verbose (bool): If True, print a human-readable trace of the step.
Returns:
str: The generated candidate sequence.
Raises:
NotImplementedError: If a subclass does not override this method.
"""
raise NotImplementedError(
f"{self.__class__.__name__} must implement generate()."
)
def __repr__(self) -> str:
return f"{self.__class__.__name__}()"
if __name__ == '__main__':
pass