
Imagine you are planning a road trip: you have to choose the best route to pick up all of your friends, reach every destination you want to visit, decide where to stop for fuel, and adapt if there's traffic or bad weather. Now imagine doing that not just for one car – but for thousands of delivery trucks moving across a continent. That's where automatic planning comes in: it lets machines figure out what actions to take to reach a goal efficiently, even when the possibilities are far too complex for humans to work out by hand. It's about giving computers the ability to reason about actions and goals, to understand what must happen first, what can go wrong, and what steps will lead from the present to a desired outcome.
Automated planning is key in many application domains, ranging between robotics, logistics and supply‑chain optimization, vehicle routing, smart grid management and energy distribution optimization, among others. Even scientific research problems in areas such as quantum computing and organic chemistry, can be framed and solved as planning problems.
In academia, researchers are often interested in developing generalized planning algorithms, i.e., planners which can efficiently solve any well-structured planning problem, regardless of the domain. Unfortunately, their generalized nature prevents these algorithms from exploiting the peculiarities of the problem. In contrast, tailored planning algorithms can often find solutions orders of magnitude faster, and solve problems which are orders of magnitude larger. The problem is that tailoring planning algorithms to a specific problem domain can be extremely time-consuming and requires deep domain expertise!
This makes writing tailored planning algorithms an excellent testing ground for our system, the Hive. Using the Hive, we can automatically discover high-performing planning algorithms specifically tailored towards any given planning domain, significantly reducing development time and effort.
The Airbus Beluga™ AI Challenge
At the beginning of the year, we decided to put this to the test by participating in the Airbus Beluga™ AI Challenge held by TUPLES. This competition required participants to find solutions to a logistic planning problem proposed by Airbus, which involved the transportation, storage, and management of aircraft parts to successfully complete a preset production schedule for constructing new aircrafts.
There are two key difficulties to this problem domain:
- Limited number of resources: There are only a limited number of racks and trailers available to transport and store aircraft parts. Poor management of these resources can lead to situations where progress is blocked unless previous actions are reversed.
- Problem scale: This competition explores the scale of real-world logistic planning problems, and the difficulties arising from the combinatorial explosion of possible actions one can take. Some of the problems explored in this competition involve over 800 aircraft parts!
This is a particularly challenging planning domain — even state-of-the-art planners can only solve the smallest and simplest problem instances. What makes it especially interesting as a testing ground is that it's completely new. At the time of our experiments, LLMs had never been trained on this type of problem. That means any solution the Hive produced isn't drawn from memorized data, but from genuine extrapolative reasoning — reasoning its way through a problem domain they've never seen before.
Despite these challenges, the Hive was able to discover a planning algorithm which solves all of the example problems supplied by the competition. This demonstrates orders-of-magnitude improvements over current state-of-the-art methods, ultimately winning us first place in both the Scalability Deterministic and the Scalability Probabilistic challenges.
Methodology
Using the Hive, we evolve a planning strategy for the Beluga planning domain, i.e., a function which encodes a decision tree for what action to take next given its current state.
Notably, we initialized the Hive from scratch with nothing more than a function header, and asked it to find a planning algorithm which solves as many problems as possible (out of a training set of 270 problems of varying difficulties), and in as few actions as possible.
policy_planner_initial.py
▾
import problem_logic as pl
def policy(problem: pl.Instance) -> pl.State:
return problem.get_initial_state()
Apart from the Python API encoding the problem logic and a short description of the problem, we provide no other guidance or hints to the Hive. After training, we evaluate the generated planning algorithm against a different set of 269 problems.
Results
Remarkably, starting from the naïve template function shown in Figure 1, that solves no problems, the Hive was able to – completely independently – find a planning algorithm which solves all of the 269 evaluation problems.
Figure 1: Comparison of planners on Airbus Beluga planning domain
We compare the Hive’s solution against two baseline LLM-based-methods, both relying on Gemini 2.5 Pro:
- Direct plan: For each problem instance, we ask Gemini 2.5 Pro to directly produce a plan to solve the problem using a few-shot prompting technique.
- Planning algorithm: Similar to the Hive, we ask Gemini 2.5 Pro to produce a planning strategy to solve problems in the Beluga planning domain.
In Figure 2, we see that both of these LLM-baseline methods perform poorly, which aligns with the evidence in academic literature that even the most powerful modern LLMs are unable to perform complex, multi-stage planning alone1,2,3,4. Therefore, the Hive, which builds on top of LLMs, is key to obtaining novel, robust, and breakthrough algorithmic discoveries.
We also compare against a state-of-the-art generalised planning algorithm, best-first width search (BFWS). Again, we see that the planning algorithm discovered by the Hive solves significantly more problems, and problems of much higher complexity. Additionally, the planning algorithm of the Hive finds solutions much faster than BFWS – for problems which both planners were able to solve, the Hive found solutions over 1000 times faster on average!
The Hive's Planning Algorithm
The planning algorithm discovered by the Hive spanned almost 500 lines of code, which we make available in this repository. In Figure 2, we show the trajectory of solutions found by the Hive, and highlight some of its notable algorithmic discoveries.
Figure 2: Evolution trajectory of the Hive's final planning algorithm
- Implement iterative, priority-driven Beluga solver policy: The initial implementation of the planning policy uses an iterative, greedy strategy to solve the Beluga problem by applying a sequence of prioritized actions. These actions are grouped into high-priority tasks like production line fulfillment and incoming jig management, and medium-priority tasks for empty jig handling and outgoing flights.
policy_planner_1.patch
▾
@@ -5,6 +5,151 @@
import problem_logic as pl
def policy(problem: pl.BelugaInstance) -> pl.BelugaState:
- return problem.get_initial_state()
+ """Solves the Beluga problem by applying a priority-based greedy strategy."""
+ current_state = problem.get_initial_state()
+
+ while not problem.is_problem_solved(current_state)[0]:
+ made_progress_this_turn = False
+
+ # --- High Priority: Production Line Fulfillment & Incoming Jigs ---
+
+ # 1. Deliver full jigs from factory trailers to hangars if a production line needs it
+ # Iterate over production lines to fulfill immediate needs
+ for pl_idx in range(len(problem.production_lines)):
+ if problem.deliver_to_hangar_preconditions(current_state, pl_idx)[0]:
+ current_state = problem.deliver_to_hangar(current_state, pl_idx)
+ made_progress_this_turn = True
+ break # Re-evaluate priorities after state change (e.g., another jig for PL or empty hangar slot)
+ if made_progress_this_turn:
+ continue
+
+ # 2. Pick up required full jigs from racks (factory side) to factory trailers
+ # Find if any production line needs a jig that isn't in factory trailers
+ required_jigs_for_pl_names = set()
+ for pl_idx, pl_ptr in enumerate(current_state.production_lines_indices):
+ if pl_ptr < len(problem.production_lines[pl_idx].schedule):
+ required_jig_name = problem.production_lines[pl_idx].schedule[pl_ptr].name
+ # Only care if it's not already in factory trailers
+ if required_jig_name not in current_state.factory_trailers.full_jigs:
+ required_jigs_for_pl_names.add(required_jig_name)
+
+ # Try to pick up any of these required jigs from a rack (factory side)
+ if len(required_jigs_for_pl_names) > 0:
+ for rack_idx in range(len(current_state.racks)):
+ rack_content = current_state.racks[rack_idx]
+ # Check if the jig at the factory side edge is one of the required ones
+ if len(rack_content) > 0 and rack_content[-1] in required_jigs_for_pl_names:
+ # Also check if it's actually a full jig, though 'jigXXX' format makes this implicit
+ if not problem.is_jig_empty(rack_content[-1]):
+ if problem.pick_up_rack_preconditions(current_state, rack_idx, False)[0]: # False for factory side
+ current_state = problem.pick_up_rack(current_state, rack_idx, False)
+ made_progress_this_turn = True
+ break # Re-evaluate priorities
+
+ if made_progress_this_turn:
+ continue
+
+ # 3. Unload incoming jigs from Beluga to Beluga trailers
+ if problem.unload_beluga_preconditions(current_state)[0]:
+ current_state = problem.unload_beluga(current_state)
+ made_progress_this_turn = True
+ continue
+
+ # 4. Put full jigs from Beluga trailers into racks (beluga side)
+ # Empty Beluga trailers to make space for more incoming jigs,
+ # and move jigs towards general storage/factory side
+ put_down_beluga_full_jig = False
+ # Iterate over full jigs in beluga trailers (sorted for deterministic behavior)
+ for jig_name in sorted(current_state.beluga_trailers.full_jigs):
+ # Try to put this jig into any available rack from the beluga side
+ for rack_idx in range(len(current_state.racks)):
+ if problem.put_down_rack_preconditions(current_state, rack_idx, jig_name, True)[0]: # True for beluga side
+ current_state = problem.put_down_rack(current_state, rack_idx, jig_name, True)
+ made_progress_this_turn = True
+ put_down_beluga_full_jig = True
+ break # Jig placed, re-evaluate priorities
+ if put_down_beluga_full_jig:
+ break # Only place one jig at a time to allow re-evaluation
+
+ if made_progress_this_turn:
+ continue
+
+ # --- Medium Priority: Empty Jig Management & Outgoing Jigs ---
+
+ # 5. Get empty jigs from hangars to factory trailers
+ # These are generated from delivered full jigs and are needed for outgoing flights
+ for jig_type_idx in range(len(current_state.hangars)):
+ if problem.get_from_hangar_preconditions(current_state, jig_type_idx)[0]:
+ current_state = problem.get_from_hangar(current_state, jig_type_idx)
+ made_progress_this_turn = True
+ break # Re-evaluate priorities
+ if made_progress_this_turn:
+ continue
+
+ # 6. Put empty jigs from factory trailers into racks (factory side)
+ # Clears factory trailers and moves empty jigs towards general storage
+ put_down_factory_empty_jig = False
+ # Iterate over types of empty jigs in factory trailers
+ for jig_type_idx in range(len(current_state.factory_trailers.empty_jigs)):
+ if current_state.factory_trailers.empty_jigs[jig_type_idx] > 0: # If there's an empty jig of this type
+ empty_jig_str = str(jig_type_idx) # Empty jigs are represented by string of their type index
+ for rack_idx in range(len(current_state.racks)):
+ # Check if space is available to put it at factory side
+ if problem.put_down_rack_preconditions(current_state, rack_idx, empty_jig_str, False)[0]: # False for factory side
+ current_state = problem.put_down_rack(current_state, rack_idx, empty_jig_str, False)
+ made_progress_this_turn = True
+ put_down_factory_empty_jig = True
+ break # Jig placed, re-evaluate priorities
+ if put_down_factory_empty_jig:
+ break
+ if made_progress_this_turn:
+ continue
+
+
+ # 7. Pick up empty jigs from racks (beluga side) to Beluga trailers for outgoing flights
+ pick_up_empty_for_beluga_outgoing = False
+ # Check if there's an outgoing jig to load for the current flight
+ if current_state.flight_index < len(problem.flights) and + current_state.flight_outgoing_index < len(problem.flights[current_state.flight_index].outgoing):
+
+ # Determine the type of empty jig needed for the current outgoing flight
+ outgoing_jig_type_int = problem.outgoing_types[current_state.flight_index]
+ # Only proceed if we actually need more empty jigs of this type in beluga trailers
+ # (i.e., we don't have enough to satisfy load_beluga yet)
+ if current_state.beluga_trailers.empty_jigs[outgoing_jig_type_int] <= + len(problem.flights[current_state.flight_index].outgoing) - current_state.flight_outgoing_index:
+
+ outgoing_jig_type_str = str(outgoing_jig_type_int)
+
+ for rack_idx in range(len(current_state.racks)):
+ rack_content = current_state.racks[rack_idx]
+ # Check if the jig at the beluga side edge is the type we need
+ if len(rack_content) > 0 and rack_content[0] == outgoing_jig_type_str:
+ if problem.pick_up_rack_preconditions(current_state, rack_idx, True)[0]: # True for beluga side
+ current_state = problem.pick_up_rack(current_state, rack_idx, True)
+ made_progress_this_turn = True
+ pick_up_empty_for_beluga_outgoing = True
+ break # Jig picked up, re-evaluate priorities
+
+ if made_progress_this_turn:
+ continue
+
+ # 8. Load outgoing jigs from Beluga trailers to the Beluga
+ if problem.load_beluga_preconditions(current_state)[0]:
+ current_state = problem.load_beluga(current_state)
+ made_progress_this_turn = True
+ continue
+
+ # 9. Complete current Beluga flight (critical to move to the next flight)
+ if problem.beluga_complete_preconditions(current_state)[0]:
+ current_state = problem.beluga_complete(current_state)
+ made_progress_this_turn = True
+ continue
+
+ # If no progress was made in this iteration, we might be stuck or the problem is implicitly solved.
+ # Break the loop to avoid infinite loops if no valid actions are possible.
+ if not made_progress_this_turn:
+ break
+
+ return current_state
- Add rack unblocking logic for Beluga and production: The logic for picking up empty jigs needed for outgoing flights was refined by systematically unblocking them from racks. Additionally, new strategies are implemented to unblock required jigs for production lines, including moving blocking jigs to other racks or freeing up trailer space.
policy_planner_6.patch
▾
@@ -25,25 +25,65 @@
made_progress_this_turn = True
continue
- # 3. Pick up empty jigs needed for outgoing flights
+ # 3. Pick up empty jigs needed for outgoing flights (and unblock them)
if (current_state.flight_index < len(problem.flights) and
current_state.flight_outgoing_index < len(problem.flights[current_state.flight_index].outgoing)):
outgoing_jig_type_int = problem.outgoing_types[current_state.flight_index]
- # Check if we need more empty jigs of this type
- needed_count = len(problem.flights[current_state.flight_index].outgoing) - current_state.flight_outgoing_index
- if current_state.beluga_trailers.empty_jigs[outgoing_jig_type_int] < needed_count:
+
+ needed_count = (len(problem.flights[current_state.flight_index].outgoing) -
+ current_state.flight_outgoing_index -
+ current_state.beluga_trailers.empty_jigs[outgoing_jig_type_int])
+
+ if needed_count > 0:
outgoing_jig_type_str = str(outgoing_jig_type_int)
- # Look for this type of empty jig on beluga side of racks
- for rack_idx in range(len(current_state.racks)):
- rack_content = current_state.racks[rack_idx]
+ # First, try direct pickup
+ for rack_idx, rack_content in enumerate(current_state.racks):
if (len(rack_content) > 0 and rack_content[0] == outgoing_jig_type_str and
problem.pick_up_rack_preconditions(current_state, rack_idx, True)[0]):
current_state = problem.pick_up_rack(current_state, rack_idx, True)
made_progress_this_turn = True
break
- if made_progress_this_turn:
- continue
+
+ # If direct pickup is not possible, try to unblock one.
+ if not made_progress_this_turn:
+ # Find the shallowest (closest to beluga side) required empty jig.
+ best_location = None # (rack_idx, pos)
+ min_blockers = float('inf')
+ for rack_idx, rack_content in enumerate(current_state.racks):
+ try:
+ pos = rack_content.index(outgoing_jig_type_str)
+ if pos > 0 and pos < min_blockers: # It's blocked
+ min_blockers = pos
+ best_location = (rack_idx, pos)
+ except ValueError:
+ continue
+
+ if best_location:
+ rack_idx, _ = best_location
+ # Try to move the single blocking jig at the edge to make progress
+ if problem.pick_up_rack_preconditions(current_state, rack_idx, True)[0]:
+ blocking_jig = current_state.racks[rack_idx][0]
+ temp_state = problem.pick_up_rack(current_state, rack_idx, True)
+
+ placed = False
+ # Try to place the blocking jig in another rack, preferring the beluga side.
+ for other_rack_idx in range(len(temp_state.racks)):
+ if other_rack_idx != rack_idx and problem.put_down_rack_preconditions(temp_state, other_rack_idx, blocking_jig, True)[0]:
+ current_state = problem.put_down_rack(temp_state, other_rack_idx, blocking_jig, True)
+ placed = True
+ made_progress_this_turn = True
+ break
+
+ if not placed:
+ for other_rack_idx in range(len(temp_state.racks)):
+ if other_rack_idx != rack_idx and problem.put_down_rack_preconditions(temp_state, other_rack_idx, blocking_jig, False)[0]:
+ current_state = problem.put_down_rack(temp_state, other_rack_idx, blocking_jig, False)
+ placed = True
+ made_progress_this_turn = True
+ break
+ if made_progress_this_turn:
+ continue
# --- Production Line Management ---
- Implement systematic unblocking for production jigs: The logic handling picking up required jigs for production lines from racks was rewritten. It now identifies all blocked required jigs, prioritizes them, and iteratively moves blocking jigs until a target jig can be directly picked up.
policy_planner_7.patch
▾
@@ -128,60 +128,93 @@
# If no direct pickup, try to unblock required jigs systematically
if not made_progress_this_turn:
- # For each rack, check if any required jig is blocked
- for rack_idx in range(len(current_state.racks)):
- rack_content = current_state.racks[rack_idx]
- # Find the positions of required jigs in this rack
- required_positions = []
+ # Create a map of where each required jig is located and how many it's blocking
+ jig_locations = {}
+ for rack_idx, rack_content in enumerate(current_state.racks):
for pos, jig in enumerate(rack_content):
if jig in required_jigs_for_pl_names and not problem.is_jig_empty(jig):
- required_positions.append(pos)
-
- if not required_positions:
- continue
-
- # Find the shallowest (closest to factory side) required jig
- shallowest_pos = max(required_positions) # Factory side is at the end
-
- # If not at the edge, we need to clear blocking jigs
- if shallowest_pos < len(rack_content) - 1:
- # We need to clear all jigs from shallowest_pos+1 to the end
- blocking_jigs = []
- for blocking_pos in range(len(rack_content) - 1, shallowest_pos, -1):
- blocking_jigs.append((blocking_pos, rack_content[blocking_pos]))
-
- # Try to move blocking jigs one by one
- for blocking_pos, blocking_jig in blocking_jigs:
- if problem.pick_up_rack_preconditions(current_state, rack_idx, False)[0]:
- temp_state = problem.pick_up_rack(current_state, rack_idx, False)
- # Try to put the blocking jig in another rack (prefer factory side)
- placed = False
- # First try factory trailers side placement
- if not placed and len(temp_state.factory_trailers) < problem.num_factory_trailers:
- for other_rack_idx in range(len(temp_state.racks)):
- if other_rack_idx != rack_idx:
- if problem.put_down_rack_preconditions(temp_state, other_rack_idx, blocking_jig, False)[0]:
- current_state = problem.put_down_rack(temp_state, other_rack_idx, blocking_jig, False)
- made_progress_this_turn = True
+ # Count how many jigs this one is blocking
+ blocking_count = len(rack_content) - pos - 1
+ jig_locations[jig] = (rack_idx, pos, blocking_count)
+
+ # Try to access each required jig, starting with those that are blocking the most other jigs
+ for required_jig in sorted(jig_locations, key=lambda x: -jig_locations[x][2]):
+ rack_idx, jig_pos, _ = jig_locations[required_jig]
+ rack_content = current_state.racks[rack_idx]
+
+ # If the jig is already at the factory side (end), we can pick it up directly
+ if jig_pos == len(rack_content) - 1:
+ if problem.pick_up_rack_preconditions(current_state, rack_idx, False)[0]:
+ current_state = problem.pick_up_rack(current_state, rack_idx, False)
+ made_progress_this_turn = True
+ break
+ else:
+ # The jig is blocked, we need to move blocking jigs
+ # Move jigs that are blocking this required jig, from the factory side towards beluga side
+ blocking_positions = list(range(len(rack_content) - 1, jig_pos, -1))
+ blocking_moved = True
+
+ # Keep trying to move blocking jigs until we can access the required jig or get stuck
+ while blocking_moved and not made_progress_this_turn:
+ blocking_moved = False
+ temp_state = current_state
+
+ for blocking_pos in blocking_positions:
+ if blocking_pos >= len(temp_state.racks[rack_idx]):
+ # This position is now empty, skip it
+ continue
+
+ blocking_jig = temp_state.racks[rack_idx][blocking_pos]
+
+ # Try to move this blocking jig
+ if problem.pick_up_rack_preconditions(temp_state, rack_idx, False)[0]:
+ pickup_state = problem.pick_up_rack(temp_state, rack_idx, False)
+
+ # Try to place it in another rack
+ placed = False
+ # First try to place in other racks on factory side
+ for target_rack_idx in range(len(pickup_state.racks)):
+ if target_rack_idx != rack_idx:
+ if problem.put_down_rack_preconditions(pickup_state, target_rack_idx, blocking_jig, False)[0]:
+ temp_state = problem.put_down_rack(pickup_state, target_rack_idx, blocking_jig, False)
+ current_state = temp_state
+ blocking_moved = True
placed = True
break
-
- # Then try beluga trailers side placement
- if not placed and len(temp_state.beluga_trailers) < problem.num_beluga_trailers:
- for other_rack_idx in range(len(temp_state.racks)):
- if other_rack_idx != rack_idx:
- if problem.put_down_rack_preconditions(temp_state, other_rack_idx, blocking_jig, True)[0]:
- current_state = problem.put_down_rack(temp_state, other_rack_idx, blocking_jig, True)
- made_progress_this_turn = True
- placed = True
+
+ # If couldn't place on factory side, try beluga side
+ if not placed:
+ for target_rack_idx in range(len(pickup_state.racks)):
+ if target_rack_idx != rack_idx:
+ if problem.put_down_rack_preconditions(pickup_state, target_rack_idx, blocking_jig, True)[0]:
+ temp_state = problem.put_down_rack(pickup_state, target_rack_idx, blocking_jig, True)
+ current_state = temp_state
+ blocking_moved = True
+ placed = True
+ break
+
+ if placed:
+ # Update blocking positions since rack content changed
+ new_rack_content = temp_state.racks[rack_idx]
+ new_jig_pos = None
+ for pos, jig in enumerate(new_rack_content):
+ if jig == required_jig:
+ new_jig_pos = pos
break
-
- if placed:
+
+ if new_jig_pos is not None:
+ blocking_positions = list(range(len(new_rack_content) - 1, new_jig_pos, -1))
+ break
+
+ # After moving one blocking jig, check if we can now pick up the required jig
+ if blocking_moved:
+ new_rack_content = temp_state.racks[rack_idx]
+ if (len(new_rack_content) > 0 and
+ new_rack_content[-1] == required_jig and
+ problem.pick_up_rack_preconditions(temp_state, rack_idx, False)[0]):
+ current_state = problem.pick_up_rack(temp_state, rack_idx, False)
+ made_progress_this_turn = True
break
- if made_progress_this_turn:
- break
- if made_progress_this_turn:
- break
# If still no progress, try a more aggressive unblocking strategy
if not made_progress_this_turn:
- Add cycle detection and backtracking to policy: This change introduces state-tracking to the policy planner workflow, enabling explicit cycle detection and emergency backtracking. The planner now tracks visited states and reverts to a previous state if a cycle is detected or if a predefined number of steps are taken without making progress.
policy_planner_11.patch
▾
@@ -7,9 +7,43 @@
def policy(problem: pl.BelugaInstance) -> pl.BelugaState:
"""Solves the Beluga problem by applying a priority-based greedy strategy."""
current_state = problem.get_initial_state()
+ seen_states = set()
+ max_steps_without_progress = 100
+ steps_without_progress = 0
while not problem.is_problem_solved(current_state)[0]:
made_progress_this_turn = False
+
+ # Check for cycle - if we've seen this state before, backtrack
+ state_signature = (
+ current_state.flight_index,
+ current_state.flight_incoming_index,
+ current_state.flight_outgoing_index,
+ current_state.production_lines_indices,
+ hash(current_state.beluga_trailers),
+ hash(current_state.factory_trailers),
+ current_state.hangars,
+ tuple(tuple(rack) for rack in current_state.racks)
+ )
+
+ if state_signature in seen_states:
+ # Backtrack to previous state and skip this action
+ if current_state.previous_state:
+ current_state = current_state.previous_state
+ continue
+ else:
+ seen_states.add(state_signature)
+ steps_without_progress = 0
+
+ # Emergency backtracking if stuck too long
+ steps_without_progress += 1
+ if steps_without_progress > max_steps_without_progress:
+ if current_state.previous_state:
+ current_state = current_state.previous_state
+ steps_without_progress = 0
+ continue
+
+ # Only proceed with normal planning if we haven't seen this state
# --- Flight Management (Highest Priority) ---
@@ -338,9 +372,14 @@
if made_progress_this_turn:
continue
- # If no progress was made, we might be stuck
+ # If no progress was made, backtrack to avoid infinite loops
if not made_progress_this_turn:
- break
+ if current_state.previous_state:
+ current_state = current_state.previous_state
+ continue
+ else:
+ # If no previous state, we're truly stuck
+ break
return current_state
- Prioritize production lines by overall schedule urgency: The production line prioritization heuristic was updated by introducing a new metric that estimates the total processing steps before a jig is required. This metric is now the primary factor for sorting production line priorities in ascending order, effectively changing the preference from higher-value components to lower-value components.
policy_planner_24.patch
▾
@@ -157,30 +157,40 @@
for pl_idx in range(len(problem.production_lines)):
remaining_jigs = len(problem.production_lines[pl_idx].schedule) - current_state.production_lines_indices[pl_idx]
if remaining_jigs > 0:
- # Calculate urgency based on remaining jigs and how aligned with current flight
+ # Calculate urgency based on remaining jigs, how aligned with current flight, and how soon the jig is needed
pl_ptr = current_state.production_lines_indices[pl_idx]
if pl_ptr < len(problem.production_lines[pl_idx].schedule):
required_jig = problem.production_lines[pl_idx].schedule[pl_ptr]
jig_type = problem.get_type_for_jig(required_jig.name)
+
+ # Calculate time until this jig is needed (how many steps until we reach this jig in the schedule)
+ time_until_needed = 0
+ for i in range(pl_idx, len(problem.production_lines)):
+ if i == pl_idx:
+ time_until_needed += (len(problem.production_lines[i].schedule) - pl_ptr)
+ else:
+ time_until_needed += len(problem.production_lines[i].schedule)
+
# Prefer jigs that match outgoing flight types to reduce Beluga loading conflicts
flight_alignment = 0
if (current_state.flight_index < len(problem.outgoing_types) and
problem.outgoing_types[current_state.flight_index] == jig_type):
flight_alignment = 1
- # Priority: flight alignment first, then remaining jigs
- priority_score = (flight_alignment, remaining_jigs, pl_idx)
+ # Priority: time until needed first, then flight alignment, then remaining jigs
+ priority_score = (time_until_needed, flight_alignment, remaining_jigs, pl_idx)
pl_priorities.append((priority_score, pl_idx))
- # Sort by priority score (alignment first, then remaining jigs)
- pl_priorities.sort(reverse=True)
+ # Sort by priority score (time until needed first, then alignment, then remaining jigs)
+ pl_priorities.sort()
for _, pl_idx in pl_priorities:
if problem.deliver_to_hangar_preconditions(current_state, pl_idx)[0]:
current_state = problem.deliver_to_hangar(current_state, pl_idx)
made_progress_this_turn = True
break
if made_progress_this_turn:
continue
# 5. Get empty jigs from hangars to factory trailers
Conclusion
LLMs alone lack the systematic reasoning and long-horizon planning capabilities needed for complex problems. However, when embedded within an evolutionary framework like the Hive, where generated algorithms are continuously evaluated, refined, and improved based on concrete performance metrics, they can discover solutions that outperform both general-purpose classical planners and rival expert-designed domain-specific algorithms. By providing nothing more than the problem domain and evaluation metrics, the Hive discovered sophisticated techniques that may have otherwise required months of expert development.
The Hive is particularly powerful for novel domains where classical planners struggle due to combinatorial explosion, and where domain expertise for manual algorithm design is limited or expensive. As planning problems continue to grow in scale and complexity, whether it’s a robot assembling a satellite in orbit, or an AI system designing a new drug, tools like the Hive agent which can automatically discover tailored, high-performing solutions will become increasingly valuable.