Skip to content

Commit 93c91ae

Browse files
author
=
committed
fixed deep recursion issue
1 parent 548c3bc commit 93c91ae

File tree

3 files changed

+199
-56
lines changed

3 files changed

+199
-56
lines changed

sdk/fracton/core/bifractal_trace.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,34 @@ def record_error(self, func: Callable, context: ExecutionContext,
574574
parent_entry_id=parent_entry_id
575575
)
576576

577+
def get_entries(self) -> List[TraceEntry]:
578+
"""Get all trace entries."""
579+
return self.recorder.get_entries()
580+
581+
def get_operation_count(self) -> int:
582+
"""Get the number of recorded operations."""
583+
return len(self.recorder.get_entries())
584+
585+
def is_empty(self) -> bool:
586+
"""Check if the trace is empty."""
587+
return self.get_operation_count() == 0
588+
589+
def get_operation(self, operation_id: str) -> Optional[Dict[str, Any]]:
590+
"""Get operation data by ID."""
591+
# Try to find the entry with the given ID
592+
for entry in self.recorder.get_entries():
593+
if entry.entry_id == operation_id:
594+
return {
595+
"operation_type": entry.function_name,
596+
"context": {
597+
"entropy": entry.context.entropy if entry.context else 0.5,
598+
"depth": entry.context.depth if entry.context else 0
599+
},
600+
"input_data": entry.parameters,
601+
"output_data": entry.result if hasattr(entry, 'result') else {}
602+
}
603+
return None
604+
577605
def get_forward_trace(self) -> List[TraceEntry]:
578606
"""Get forward trace (chronological order)."""
579607
return self.recorder.get_entries()

sdk/fracton/core/recursive_engine.py

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,30 @@
88

99
import time
1010
import uuid
11+
from enum import Enum
12+
from typing import Union, Callable
1113
from typing import Any, Callable, Dict, List, Optional, Union
1214
from dataclasses import dataclass, field
1315
from contextlib import contextmanager
1416
import threading
1517
from collections import deque
1618

1719

20+
class TrampolineResult(Enum):
21+
"""Enumeration for trampoline execution results."""
22+
CONTINUE = "continue"
23+
COMPLETE = "complete"
24+
25+
26+
@dataclass
27+
class Continuation:
28+
"""Represents a continuation in the trampoline execution."""
29+
func: Callable
30+
memory: Any
31+
context: 'ExecutionContext'
32+
result_type: TrampolineResult = TrampolineResult.CONTINUE
33+
34+
1835
@dataclass
1936
class ExecutionContext:
2037
"""
@@ -291,28 +308,70 @@ def execute(self, func: Callable, memory: Any, context: Union[ExecutionContext,
291308

292309
# Handle tail recursion optimization
293310
if self._is_tail_recursive_call(func):
294-
# Reuse current stack frame
295-
result = func(memory, exec_context)
311+
# Use trampoline for tail recursion
312+
return self._execute_trampoline(func, memory, exec_context)
296313
else:
297-
# Push new frame and execute
314+
# Push new frame and execute with trampoline
298315
self.call_stack.push(func, exec_context, exec_context.trace_id)
299316
try:
300-
result = func(memory, exec_context)
317+
return self._execute_trampoline(func, memory, exec_context)
301318
finally:
302319
self.call_stack.pop()
303320

304-
# Update statistics
305-
execution_time = time.time() - start_time
306-
self._update_stats(func, exec_context, execution_time)
307-
308-
return result
309-
310321
except Exception as e:
311322
# Add execution context to exception
312323
if hasattr(e, 'execution_context'):
313324
e.execution_context = exec_context
314325
raise
315326

327+
def _execute_trampoline(self, func: Callable, memory: Any, context: 'ExecutionContext') -> Any:
328+
"""
329+
Execute a function using trampoline-based recursion management.
330+
331+
This prevents deep recursion by converting recursive calls into
332+
an iterative loop with continuations.
333+
"""
334+
import threading
335+
start_time = time.time()
336+
continuation_queue = deque([Continuation(func, memory, context)])
337+
result = None
338+
339+
# Mark that we're in trampoline execution
340+
current_thread = threading.current_thread()
341+
old_value = getattr(current_thread, '_fracton_in_trampoline', False)
342+
current_thread._fracton_in_trampoline = True
343+
344+
try:
345+
while continuation_queue:
346+
current = continuation_queue.popleft()
347+
348+
try:
349+
# Execute the function
350+
temp_result = current.func(current.memory, current.context)
351+
352+
# Check if result is a continuation (recursive call)
353+
if isinstance(temp_result, Continuation):
354+
# Add to queue for further processing
355+
continuation_queue.append(temp_result)
356+
else:
357+
# Final result
358+
result = temp_result
359+
360+
except Exception as e:
361+
# Add execution context to exception
362+
if hasattr(e, 'execution_context'):
363+
e.execution_context = current.context
364+
raise
365+
finally:
366+
# Restore previous thread state
367+
current_thread._fracton_in_trampoline = old_value
368+
369+
# Update statistics
370+
execution_time = time.time() - start_time
371+
self._update_stats(func, context, execution_time)
372+
373+
return result
374+
316375
def get_execution_stats(self) -> ExecutionStats:
317376
"""Get current execution statistics."""
318377
return self.stats

sdk/fracton/lang/primitives.py

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
including recursive calls, crystallization, branching, and context management.
66
"""
77

8+
import uuid
89
from typing import Any, Callable, Dict, List, Optional, Union
9-
from ..core.recursive_engine import ExecutionContext, get_default_executor
10+
from ..core.recursive_engine import ExecutionContext, get_default_executor, Continuation
1011
from ..core.bifractal_trace import BifractalTrace
1112
from ..core.memory_field import MemoryField
1213

@@ -54,31 +55,27 @@ def fibonacci(memory, context):
5455
if not hasattr(func, '_fracton_recursive') or not func._fracton_recursive:
5556
raise ValueError(f"Function {func.__name__} must be decorated with @recursive to use recurse()")
5657

57-
executor = get_default_executor()
58+
# Check if we're inside a trampoline execution
59+
import threading
60+
current_thread = threading.current_thread()
61+
in_trampoline = getattr(current_thread, '_fracton_in_trampoline', False)
5862

59-
# Record call in trace if provided
60-
if trace:
61-
entry_id = trace.record_call(func, context)
62-
63-
try:
64-
# Execute through the recursive engine
65-
result = executor.execute(func, memory, context)
66-
67-
# Record successful return
68-
trace.record_return(func, context, result, entry_id)
69-
return result
70-
71-
except Exception as e:
72-
# Record error
73-
trace.record_error(func, context, e, entry_id)
74-
raise
63+
if in_trampoline:
64+
# We're inside a trampoline, return a continuation
65+
if trace:
66+
entry_id = trace.record_call(func, context)
67+
return Continuation(func, memory, context)
7568
else:
76-
# Execute without tracing
69+
# We're called directly, execute through the executor with trampoline
70+
executor = get_default_executor()
7771
return executor.execute(func, memory, context)
7872

7973

80-
def crystallize(data: Any, patterns: Optional[List] = None,
81-
entropy_threshold: float = 0.3) -> Any:
74+
def crystallize(data: Any, memory: Optional[MemoryField] = None,
75+
context: Optional[ExecutionContext] = None,
76+
patterns: Optional[List] = None,
77+
entropy_threshold: float = 0.3,
78+
trace: Optional[BifractalTrace] = None) -> Any:
8279
"""
8380
Crystallize data into stable structures based on entropy patterns.
8481
@@ -87,75 +84,134 @@ def crystallize(data: Any, patterns: Optional[List] = None,
8784
8885
Args:
8986
data: Data to crystallize
87+
memory: Optional memory field to store crystallized data
88+
context: Optional execution context for entropy awareness
9089
patterns: Optional list of patterns to reinforce
9190
entropy_threshold: Entropy level below which crystallization occurs
91+
trace: Optional bifractal trace for recording crystallization
9292
9393
Returns:
94-
Crystallized data structure
94+
If memory is provided, returns the storage ID. Otherwise returns crystallized data.
9595
9696
Example:
9797
chaotic_data = {"values": [1, 5, 2, 8, 3, 7, 4, 6]}
9898
stable_data = fracton.crystallize(chaotic_data)
9999
# Result: {"values": [1, 2, 3, 4, 5, 6, 7, 8]} # Sorted
100100
"""
101-
# Calculate current entropy of data
102-
current_entropy = _calculate_data_entropy(data)
101+
# Use context entropy if available, otherwise calculate from data
102+
if context and hasattr(context, 'entropy'):
103+
current_entropy = context.entropy
104+
else:
105+
current_entropy = _calculate_data_entropy(data)
106+
107+
# Record in trace if provided
108+
if trace:
109+
trace_id = trace.record_operation(
110+
operation_type="crystallization",
111+
context=context,
112+
input_data={"data": data, "entropy_threshold": entropy_threshold}
113+
)
103114

104115
if current_entropy <= entropy_threshold:
105116
# Already stable enough
106-
return data
117+
crystallized_data = data
118+
else:
119+
# Apply crystallization based on data type
120+
if isinstance(data, dict):
121+
crystallized_data = _crystallize_dict(data, patterns)
122+
elif isinstance(data, list):
123+
crystallized_data = _crystallize_list(data, patterns)
124+
elif isinstance(data, str):
125+
crystallized_data = _crystallize_string(data, patterns)
126+
else:
127+
# For other types, try to find inherent order
128+
crystallized_data = _crystallize_generic(data, patterns)
107129

108-
# Apply crystallization based on data type
109-
if isinstance(data, dict):
110-
return _crystallize_dict(data, patterns)
111-
elif isinstance(data, list):
112-
return _crystallize_list(data, patterns)
113-
elif isinstance(data, str):
114-
return _crystallize_string(data, patterns)
130+
# Update trace with result if provided
131+
if trace:
132+
trace.record_operation(
133+
operation_type="crystallization_complete",
134+
context=context,
135+
output_data={"crystallized_data": crystallized_data},
136+
parent_operation=trace_id
137+
)
138+
139+
# Store in memory if provided
140+
if memory:
141+
crystal_id = f"crystal_{uuid.uuid4().hex[:8]}"
142+
memory.set(crystal_id, crystallized_data)
143+
return crystal_id
115144
else:
116-
# For other types, try to find inherent order
117-
return _crystallize_generic(data, patterns)
145+
return crystallized_data
118146

119147

120-
def branch(condition: Union[bool, Callable],
121-
if_true: Callable, if_false: Callable,
122-
memory: MemoryField, context: ExecutionContext) -> Any:
148+
def branch(condition: Union[bool, Callable, List],
149+
if_true: Union[Callable, List, MemoryField] = None,
150+
if_false: Union[Callable, ExecutionContext, List] = None,
151+
memory: MemoryField = None, context: Union[ExecutionContext, List] = None) -> Any:
123152
"""
124153
Entropy-aware conditional branching for recursive operations.
125154
126155
This function provides conditional execution that considers entropy levels
127156
and execution context for optimal path selection.
128157
129158
Args:
130-
condition: Boolean condition or function that returns boolean
131-
if_true: Function to call if condition is true
132-
if_false: Function to call if condition is false
133-
memory: Shared memory field
134-
context: Execution context
159+
condition: Boolean condition, function that returns boolean, or list of functions
160+
if_true: Function to call if condition is true, or MemoryField for multi-branch
161+
if_false: Function to call if condition is false, or contexts for multi-branch
162+
memory: Shared memory field (traditional mode)
163+
context: Execution context or list of contexts for multi-branch
135164
136165
Returns:
137-
Result of the selected branch function
166+
Result of the selected branch function, or list of results for multi-branch
138167
139168
Example:
169+
# Simple branch
140170
result = fracton.branch(
141171
context.entropy > 0.5,
142172
high_entropy_path,
143173
low_entropy_path,
144174
memory,
145175
context
146176
)
177+
178+
# Multi-branch over contexts: branch(functions_list, memory, contexts_list)
179+
results = fracton.branch(
180+
[func1, func2],
181+
memory,
182+
[context1, context2]
183+
)
147184
"""
185+
# Handle multi-branch case: branch([func1, func2], memory, [ctx1, ctx2])
186+
if isinstance(condition, list) and if_false is not None and isinstance(if_false, list):
187+
functions = condition
188+
memory_field = if_true # Second arg is memory
189+
contexts = if_false # Third arg is contexts list
190+
results = []
191+
for func, ctx in zip(functions, contexts):
192+
# For non-recursive functions, call directly
193+
if hasattr(func, '_fracton_recursive') and func._fracton_recursive:
194+
result = recurse(func, memory_field, ctx)
195+
else:
196+
result = func(memory_field, ctx)
197+
results.append(result)
198+
return results
199+
200+
# Handle traditional branch case: branch(condition, if_true, if_false, memory, context)
148201
# Evaluate condition if it's a callable
149202
if callable(condition):
150203
condition_result = condition(memory, context)
151204
else:
152205
condition_result = condition
153206

154207
# Select and execute branch
155-
if condition_result:
156-
return recurse(if_true, memory, context)
208+
selected_func = if_true if condition_result else if_false
209+
210+
# For non-recursive functions, call directly
211+
if hasattr(selected_func, '_fracton_recursive') and selected_func._fracton_recursive:
212+
return recurse(selected_func, memory, context)
157213
else:
158-
return recurse(if_false, memory, context)
214+
return selected_func(memory, context)
159215

160216

161217
def merge_contexts(*contexts: ExecutionContext) -> ExecutionContext:

0 commit comments

Comments
 (0)