-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpattern.py
More file actions
202 lines (153 loc) · 6.13 KB
/
pattern.py
File metadata and controls
202 lines (153 loc) · 6.13 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
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
195
196
197
198
199
200
201
202
# CHUNK_META:
# Purpose: Pattern class - pattern (stable neuron configuration)
# Dependencies: neuron, connection
# API: Pattern
"""
Pattern - stable configuration of neurons and connections.
According to specification (plan.md, section 7):
- Pattern = set of neurons + connections between them
- Pattern does NOT have: center, number, weight, coordinates, symbol
- Pattern exists as network configuration
- Three levels: structural, dynamic, historical
"""
from __future__ import annotations
from typing import Set, FrozenSet
from neuron import Neuron
from connection import Connection, ConnectionState
# ANCHOR: PATTERN_CLASS - pattern as structural memory unit
class Pattern:
"""
Pattern - stable configuration of neurons and connections.
Intent: Pattern = structural + dynamic + historical knowledge unit.
This is NOT a vector, NOT an embedding, NOT a number (plan.md, section 7).
Attributes:
id: Unique pattern identifier.
neurons: Set of neurons in the pattern.
connections: Set of connections within the pattern.
Note:
Pattern is formed when a group of neurons co-activated
enough times and connections became MYELINATED/USED.
"""
_id_counter: int = 0
# API_PUBLIC
def __init__(self, neurons: Set[Neuron], connections: Set[Connection] | None = None) -> None:
"""
Creates pattern from set of neurons.
Args:
neurons: Set of neurons forming the pattern.
connections: Set of connections within pattern (optional, computed automatically).
Raises:
AssertionError: If neuron set is empty.
"""
# Precondition
assert len(neurons) > 0, "pattern must contain at least one neuron"
Pattern._id_counter += 1
self.id: str = f"pattern_{Pattern._id_counter}"
self.neurons: FrozenSet[Neuron] = frozenset(neurons)
# If connections not provided, compute them from neurons
if connections is None:
self.connections: FrozenSet[Connection] = self._extract_internal_connections()
else:
self.connections = frozenset(connections)
# Postcondition
assert len(self.neurons) > 0, "pattern must contain neurons"
# API_PRIVATE
def _extract_internal_connections(self) -> FrozenSet[Connection]:
"""
Extracts connections that connect neurons within the pattern.
Returns:
Set of connections between pattern neurons.
"""
internal_connections: Set[Connection] = set()
for neuron in self.neurons:
for conn in neuron.connections_out:
if conn.to_neuron in self.neurons:
internal_connections.add(conn)
return frozenset(internal_connections)
# API_PUBLIC
def is_stable(self) -> bool:
"""
Checks if pattern is stable.
Intent: Pattern is stable if majority of its connections
are in USED or MYELINATED state.
Returns:
True if pattern is stable.
"""
if len(self.connections) == 0:
return False
stable_count = sum(
1 for conn in self.connections
if conn.state in (ConnectionState.USED, ConnectionState.MYELINATED)
)
# Pattern is stable if more than half of connections are stable
return stable_count > len(self.connections) // 2
# API_PUBLIC
def is_myelinated(self) -> bool:
"""
Checks if pattern is fully myelinated.
Returns:
True if all connections are myelinated.
"""
if len(self.connections) == 0:
return False
return all(conn.is_myelinated() for conn in self.connections)
# API_PUBLIC
def overlaps_with(self, other: Pattern) -> Set[Neuron]:
"""
Finds overlap with another pattern.
Intent: Pattern overlap creates categories/generalizations
(plan.md, section 7.4, 19).
Args:
other: Another pattern to compare.
Returns:
Set of shared neurons.
"""
# Precondition
assert isinstance(other, Pattern), "other must be Pattern"
return set(self.neurons) & set(other.neurons)
# API_PUBLIC
def overlap_ratio(self, other: Pattern) -> float:
"""
Computes overlap ratio with another pattern.
Note: This is used only for logging/debugging,
NOT for making similarity decisions (forbidden by specification).
Args:
other: Another pattern.
Returns:
Ratio of shared neurons from smaller pattern.
"""
overlap = self.overlaps_with(other)
min_size = min(len(self.neurons), len(other.neurons))
if min_size == 0:
return 0.0
return len(overlap) / min_size
# API_PUBLIC
def contains_neuron(self, neuron: Neuron) -> bool:
"""
Checks if neuron is part of the pattern.
Args:
neuron: Neuron to check.
Returns:
True if neuron is in the pattern.
"""
return neuron in self.neurons
# API_PUBLIC
def get_neuron_ids(self) -> FrozenSet[str]:
"""
Returns pattern neuron identifiers.
Returns:
Set of neuron ids.
"""
return frozenset(n.id for n in self.neurons)
def __repr__(self) -> str:
neuron_ids = sorted(n.id for n in self.neurons)[:5]
suffix = "..." if len(self.neurons) > 5 else ""
return f"Pattern({self.id}, neurons=[{', '.join(neuron_ids)}{suffix}], size={len(self.neurons)})"
def __hash__(self) -> int:
return hash(self.id)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Pattern):
return NotImplemented
return self.neurons == other.neurons
def __len__(self) -> int:
return len(self.neurons)