1+ import sys
2+ sys .path .append ('../../' )
13from dataclasses import dataclass
24import numpy as np
3- from helper import cut_from
5+ from simulation_base import SimulationConfig , TreeSimulationBase
46
57@dataclass
6- class EnvySimulationConfig :
8+ class EnvySimulationConfig ( SimulationConfig ) :
79 """Configuration for Envy trellis tree simulation parameters."""
810
9- # Tying and Pruning
11+ # Override base defaults for Envy-specific values
1012 num_iteration_tie : int = 5
1113 num_iteration_prune : int = 16
14+ pruning_age_threshold : int = 6
15+ derivation_length : int = 128
1216
13- # Display
14- label : bool = True
15-
16- # Support Structure
17+ # Envy-specific Support Structure
1718 support_trunk_wire_point = None
1819 support_num_wires : int = 14
19- support_spacing_wires : int = 1
2020
21- # Point Generation
21+ # Envy-specific Point Generation (V-trellis)
2222 trellis_x_value : float = 0.45
2323 trellis_z_start : float = 0.6
2424 trellis_z_end : float = 3.4
2525 trellis_z_spacing : float = 0.45
2626
27- # Energy and Tying Parameters
28- energy_threshold : float = 0.25
29- energy_decay_factor : float = 105
30- tolerance : float = 1e-5
31- # Energy and Tying Parameters
32- energy_distance_weight : float = 0.5 # Weight for distance in energy calculation (was hardcoded /2)
33- energy_threshold : float = 1.0 # Maximum energy threshold for tying
34-
35-
36- # Pruning Parameters
37- pruning_age_threshold : int = 6
38-
39- # L-System Parameters
40- derivation_length : int = 128
41-
42- # Growth Parameters
27+ # Envy-specific Growth Parameters
4328 growth_length : float = 0.1
4429 bud_spacing_age : int = 2
4530
46- # Visualization Parameters
47- attractor_point_width : int = 10
48-
49-
50- def generate_points_v_trellis (simulation_config ):
51- """
52- Generate 3D points for the V-trellis wire structure.
5331
54- Creates a linear array of wire attachment points along the x-axis at a fixed
55- height (z) and depth (y). The points are spaced evenly within the configured
56- z-range and used to construct the trellis support structure.
32+ class EnvySimulation (TreeSimulationBase ):
5733 """
58- x = np .full ((7 ,), simulation_config .trellis_x_value ).astype (float )
59- y = np .full ((7 ,), 0 ).astype (float )
60- z = np .arange (simulation_config .trellis_z_start ,
61- simulation_config .trellis_z_end ,
62- simulation_config .trellis_z_spacing )
63-
64- pts = []
65- for i in range (x .shape [0 ]):
66- pts .append ((- x [i ], y [i ], z [i ]))
67- pts .append ((x [i ], y [i ], z [i ]))
68- return pts
69-
70-
71-
72- def get_energy_mat (branches , arch , simulation_config ):
73- """
74- Calculate the energy matrix for optimal branch-to-wire assignment.
75-
76- This function computes an energy cost matrix where each entry represents the
77- "cost" of assigning a specific branch to a specific wire in the trellis system.
78- The energy is based on the Euclidean distance from wire attachment points to
79- both the start and end points of each branch, weighted by the simulation's
80- distance weight parameter.
34+ Envy trellis architecture simulation.
8135
82- The algorithm uses a greedy optimization approach where branches are assigned
83- to the lowest-energy available wire that hasn't reached capacity.
84-
85- Args:
86- branches: List of branch objects to be assigned to wires
87- arch: Support architecture object containing wire information
88-
89- Returns:
90- numpy.ndarray: Energy matrix of shape (num_branches, num_wires) where
91- matrix[i][j] is the energy cost of assigning branch i to wire j.
92- Untied branches and occupied wires have infinite energy (np.inf).
36+ Implements the Envy V-trellis training system with wires arranged in a V-shape
37+ on both sides of the tree row.
9338 """
94- num_branches = len (branches )
95- num_wires = len (arch .branch_supports )
96-
97- # Initialize energy matrix with infinite values (impossible assignments)
98- energy_matrix = np .full ((num_branches , num_wires ), np .inf )
99-
100- # Calculate energy costs for all valid branch-wire combinations
101- for branch_idx , branch in enumerate (branches ):
102- # Skip branches that are already tied
103- if branch .tying .has_tied :
104- continue
105-
106- for wire_id , wire in arch .branch_supports .items ():
107- # Skip wires that already have a branch attached
108- if wire .num_branch >= 1 :
109- continue
110-
111- # Calculate weighted distance energy for this branch-wire pair
112- # Energy considers distance from wire to both branch endpoints
113- wire_point = np .array (wire .point )
114- branch_start = np .array (branch .location .start )
115- branch_end = np .array (branch .location .end )
116-
117- start_distance_energy = np .sum ((wire_point - branch_start ) ** 2 )
118- end_distance_energy = np .sum ((wire_point - branch_end ) ** 2 )
119-
120- total_energy = (start_distance_energy + end_distance_energy ) * simulation_config .energy_distance_weight
121-
122- energy_matrix [branch_idx , wire_id ] = total_energy
12339
124- return energy_matrix
40+ def generate_points (self ):
41+ """
42+ Generate 3D points for the V-trellis wire structure.
12543
126- def decide_guide (energy_matrix , branches , arch , simulation_config ):
127- """
128- Perform greedy assignment of branches to wires based on energy matrix.
129-
130- This function implements a greedy optimization algorithm that iteratively assigns
131- the branch-wire pair with the lowest energy cost. Once a branch is assigned to
132- a wire, both that branch and wire are marked as unavailable (infinite energy)
133- to prevent further assignments.
134-
135- The algorithm continues until no valid assignments remain (all remaining energies
136- are infinite or above the threshold).
137-
138- Args:
139- energy_matrix: numpy.ndarray of shape (num_branches, num_wires) with energy costs
140- branches: List of branch objects to be assigned
141- arch: Support architecture containing wire information
44+ Creates a linear array of wire attachment points along the x-axis at a fixed
45+ height (z) and depth (y). The points are spaced evenly within the configured
46+ z-range and used to construct the trellis support structure.
14247
143- Returns:
144- None: Modifies branches and arch in-place with new assignments
145- """
146- num_branches , num_wires = energy_matrix .shape
147-
148- # Early return if no branches or wires to assign
149- if num_branches == 0 or num_wires == 0 :
150- return
151-
152- # Continue making assignments until no valid ones remain
153- while True :
154- # Find the minimum energy value and its position
155- min_energy_indices = np .argwhere (energy_matrix == np .min (energy_matrix ))
156-
157- # If no valid indices found or matrix is empty, stop
158- if len (min_energy_indices ) == 0 :
159- break
160-
161- # Get the first (and typically only) minimum energy position
162- branch_idx , wire_id = min_energy_indices [0 ]
163- min_energy = energy_matrix [branch_idx , wire_id ]
164-
165- # Stop if minimum energy is infinite (no valid assignments) or above threshold
166- if np .isinf (min_energy ) or min_energy > simulation_config .energy_threshold :
167- break
168-
169- # Get the branch and wire objects
170- branch = branches [branch_idx ]
171- wire = arch .branch_supports [wire_id ]
172-
173- # Skip if branch is already tied (defensive check)
174- if branch .tying .has_tied :
175- # Mark this assignment as invalid and continue
176- energy_matrix [branch_idx , wire_id ] = np .inf
177- continue
178-
179- # Perform the assignment
180- branch .tying .guide_target = wire
181- wire .add_branch ()
182-
183- # Mark branch and wire as unavailable for future assignments
184- # Set entire row (branch) to infinity - this branch can't be assigned again
185- energy_matrix [branch_idx , :] = np .inf
186- # Set entire column (wire) to infinity - this wire can't accept more branches
187- energy_matrix [:, wire_id ] = np .inf
188-
189-
190- def prune (lstring , simulation_config ):
191- """
192- Prune old branches that exceed the age threshold and haven't been tied to wires.
193-
194- This function implements the pruning strategy for the tree training simulation.
195- It identifies branches that have grown too old (exceeding the pruning age threshold)
196- but haven't been successfully tied to trellis wires. Such branches are considered
197- unproductive and are removed from the L-System to encourage new growth.
198-
199- The pruning criteria are:
200- 1. Branch age exceeds the configured pruning threshold
201- 2. Branch has not been tied to any trellis wire
202- 3. Branch has not already been marked for cutting
203-
204- When a branch meets all criteria, it is:
205- - Marked as cut (to prevent re-processing)
206- - Removed from the L-System string using cut_from()
207-
208- Args:
209- lstring: The current L-System string containing modules and their parameters
210-
211- Returns:
212- bool: True if a branch was pruned, False if no eligible branches found
213-
214- Note:
215- This function processes one branch at a time and returns immediately after
216- pruning a single branch. It should be called repeatedly (e.g., in a while loop)
217- until no more pruning operations are possible. The cut_from() function handles
218- the actual removal of the branch and any dependent substructures from the string.
219- """
220- for position , symbol in enumerate (lstring ):
221- # Check if this is a WoodStart module (represents a branch)
222- if symbol .name == 'WoodStart' :
223- branch = symbol [0 ].type
224-
225- # Check pruning criteria
226- age_exceeds_threshold = branch .info .age > simulation_config .pruning_age_threshold
227- not_tied_to_wire = not branch .tying .has_tied
228- not_already_cut = not branch .info .cut
229- is_prunable = branch .info .prunable
230-
231- # Prune if all criteria are met
232- if age_exceeds_threshold and not_tied_to_wire and not_already_cut and is_prunable :
233- # Mark branch as cut to prevent re-processing
234- branch .info .cut = True
48+ Returns:
49+ list: List of (x, y, z) tuples representing wire attachment points in V-trellis formation
50+ """
51+ x = np .full ((7 ,), self .config .trellis_x_value ).astype (float )
52+ y = np .full ((7 ,), 0 ).astype (float )
53+ z = np .arange (self .config .trellis_z_start ,
54+ self .config .trellis_z_end ,
55+ self .config .trellis_z_spacing )
56+
57+ pts = []
58+ for i in range (x .shape [0 ]):
59+ pts .append ((- x [i ], y [i ], z [i ]))
60+ pts .append ((x [i ], y [i ], z [i ]))
61+ return pts
62+
63+
64+ # Backwards compatibility: provide standalone functions that use the class
65+ def generate_points_v_trellis (simulation_config ):
66+ """Backward compatibility wrapper for generate_points."""
67+ sim = EnvySimulation (simulation_config )
68+ return sim .generate_points ()
23569
236- # Remove the branch from the L-System string
237- print ("Pruning branch at position:" , position , lstring [position ]) # Debug statement
238- lstring = cut_from (position , lstring )
70+ def get_energy_mat (branches , arch , simulation_config ):
71+ """Backward compatibility wrapper for get_energy_mat."""
72+ sim = EnvySimulation (simulation_config )
73+ return sim .get_energy_mat (branches , arch )
23974
240- return True
75+ def decide_guide (energy_matrix , branches , arch , simulation_config ):
76+ """Backward compatibility wrapper for decide_guide."""
77+ sim = EnvySimulation (simulation_config )
78+ return sim .decide_guide (energy_matrix , branches , arch )
24179
242- return False
80+ def prune (lstring , simulation_config ):
81+ """Backward compatibility wrapper for prune."""
82+ sim = EnvySimulation (simulation_config )
83+ return sim .prune (lstring )
24384
24485def tie (lstring , simulation_config ):
245- """
246- Perform tying operation on eligible branches in the L-System string.
247-
248- This function searches through the L-System string for 'WoodStart' modules that
249- represent branches ready for tying to trellis wires. It identifies branches that:
250- 1. Have tying properties (tying attribute exists)
251- 2. Have a defined tie axis (tie_axis is not None)
252- 3. Have not been tied yet (tie_updated is False)
253- 4. Have guide points available for wire attachment
254-
255- When an eligible branch is found, it performs the tying operation by:
256- - Marking the branch as tied (tie_updated = False)
257- - Adding the branch to the target wire
258- - Calling the branch's tie_lstring method to modify the L-System string
259-
260- Args:
261- lstring: The current L-System string containing modules and their parameters
262-
263- Returns:
264- bool: True if a tying operation was performed, False if no eligible branches found
265-
266- Note:
267- This function processes one branch at a time and returns immediately after
268- tying a single branch. It should be called repeatedly (e.g., in a while loop)
269- until no more tying operations are possible.
270- """
271- for position , symbol in enumerate (lstring ):
272- # Check if this is a WoodStart module with tying capabilities
273- if (symbol == 'WoodStart' and
274- hasattr (symbol [0 ].type , 'tying' ) and
275- getattr (symbol [0 ].type .tying , 'tie_axis' , None ) is not None ):
276-
277- branch = symbol [0 ].type
278-
279- # Skip branches that have already been processed for tying
280- if not branch .tying .tie_updated :
281- continue
282-
283- # Check if branch has guide points for wire attachment
284- if branch .tying .guide_points :
285- # Perform the tying operation
286- branch .tying .tie_updated = False
287- branch .tying .guide_target .add_branch ()
288-
289- # Update the L-System string with tying modifications
290- lstring , modifications_count = branch .tie_lstring (lstring , position )
291-
292- return True
86+ """Backward compatibility wrapper for tie."""
87+ sim = EnvySimulation (simulation_config )
88+ return sim .tie (lstring )
89+
29390
294- return False
0 commit comments