Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
ca6dc91
set up capoeira
finitearth Dec 27, 2025
1bc1c72
copy pased optimizer from research repo
finitearth Dec 27, 2025
732d5c0
Simplify CAPO helper usage and adjust tests
finitearth Dec 27, 2025
6ee1860
Merge branch 'feature/welcome_capoeira' into codex/add-capoeira-optim…
finitearth Dec 27, 2025
3961739
debugging
finitearth Dec 27, 2025
b64b53d
delete redundant util file
finitearth Dec 27, 2025
01e5b6c
Align Capoeira with promptolution interface
finitearth Dec 27, 2025
f65deef
reduce code complexity
finitearth Dec 27, 2025
c532b48
clean up for pre commit
finitearth Dec 27, 2025
79395a0
Potential fix for pull request finding 'Wrong name for an argument in…
finitearth Dec 28, 2025
0bb5e7d
minor fixes
finitearth Dec 28, 2025
09a2328
Merge branch 'feature/welcome_capoeira' of https://github.com/automl/…
finitearth Dec 28, 2025
f152077
fix tests
finitearth Dec 28, 2025
cffa789
implemented evalresults class
finitearth Dec 29, 2025
6d461a9
Delete .vscode/settings.json
finitearth Dec 29, 2025
4063f24
Update coverage badge in README [skip ci]
finitearth Dec 29, 2025
8411933
refining capoeira
finitearth Jan 3, 2026
3bb14ef
further refinements
finitearth Jan 5, 2026
f0caca0
minor clean up
finitearth Jan 5, 2026
09cd805
refine testing
finitearth Jan 6, 2026
505702d
parse kwargs to reward functions"
finitearth Jan 6, 2026
d371a67
create scalarization fallback for multi objective
finitearth Jan 6, 2026
cb0b882
refine mocapo
finitearth Jan 7, 2026
2cd5ef2
remove task description form get optimizer to fix circular imports
finitearth Jan 8, 2026
cfc0622
use task type to judge task type
finitearth Jan 8, 2026
79d654d
change tokenizer handling to work with new hf interface
Jan 8, 2026
3719f84
change sampling params from vllm
Jan 8, 2026
9533232
change automatic batch size alignment of vllm
Jan 8, 2026
9e33e3a
remove abstractmethod
Jan 8, 2026
016b298
allow for tournament select
finitearth Jan 9, 2026
e2cd177
change init of f_old to inf
finitearth Jan 9, 2026
8beca49
impelment comments
finitearth Jan 12, 2026
f85062b
incoeprated comments
finitearth Jan 13, 2026
52d29f6
fix parent selection
finitearth Jan 13, 2026
1a25c51
fix token counting
finitearth Jan 13, 2026
98214cd
change tokenizer
finitearth Jan 13, 2026
21a612d
revert
finitearth Jan 13, 2026
c0d13cc
fix token counting
finitearth Jan 13, 2026
a3affe3
revert
finitearth Jan 13, 2026
a4e2476
bye capoeira
finitearth Jan 14, 2026
c0f02be
green test
finitearth Jan 14, 2026
e9cd844
change get evaluated blocks function to work also with one prompt
Jan 15, 2026
5e74532
allow for empty cache at key
Jan 15, 2026
a210183
some more cache issues
Jan 15, 2026
745f722
change comput cost function
Jan 15, 2026
05d5ebc
some fixes
Jan 16, 2026
70ae296
fix compute costs function
Jan 16, 2026
276e5fa
fix token count
Jan 16, 2026
83a6f9d
fix tracking of blocks
finitearth Jan 16, 2026
4bcda09
fix block idx subsampling
finitearth Jan 18, 2026
ace326e
allow for y_column in reward task
Jan 23, 2026
131340b
Merge branch 'feature/welcome_capoeira' of https://github.com/finitea…
Jan 23, 2026
36ce80e
formatting
Feb 15, 2026
6ecbfff
fixes in mo task and base task block idx handling
Feb 15, 2026
3368665
add import since it is requried for test cases
Feb 15, 2026
a4136f9
Update coverage badge in README [skip ci]
mo374z Feb 15, 2026
0b75d1e
change wording
Feb 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions promptolution/optimizers/capoeira.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ def _step(self) -> List[Prompt]:
return self.prompts

def _do_intensification(self, challenger: Prompt) -> None:
if challenger in self.incumbents:
return
if challenger in self.non_incumbents:
# remove from non-incumbents to re-evaluate
self.non_incumbents.remove(challenger)

common_blocks = self._get_common_blocks(self.incumbents)

# bootstrap if no common blocks yet
Expand Down Expand Up @@ -330,8 +336,9 @@ def _select_survivors(self) -> None:
dists = self._calculate_crowding_distance(worst_front_vecs)

# Find index relative to the worst front list
local_worst_idx = int(np.argmin(dists))
# Map back to the main challenger list index
min_dist = np.min(dists)
tied_indices = np.where(dists == min_dist)[0]
local_worst_idx = np.random.choice(tied_indices)
victim_idx = worst_front_indices[local_worst_idx]

self.non_incumbents.pop(victim_idx)
Expand Down Expand Up @@ -369,12 +376,52 @@ def _select_parent_from_pool(self, selection_pool: List[Prompt]) -> Prompt:
if p2 in self.incumbents:
return p2

# both are non-incumbents
blocks_map = self.task.get_evaluated_blocks([p1, p2])
blocks1 = blocks_map.get(str(p1), set())
blocks2 = blocks_map.get(str(p2), set())

if blocks1 == blocks2: # both evaluated on same blocks
# use NDS + Crowding Distance
self.task.set_block_idx(list(sorted(blocks1)))
res = self.task.evaluate([p1, p2], self.predictor)
# check if dominated
vecs = self._get_objective_vectors(res)
if self._is_dominated(vecs[0], vecs[1]):
return p2
if self._is_dominated(vecs[1], vecs[0]):
return p1
# tie-breaker: crowding distance
distances = self._calculate_crowding_distance(vecs)
if distances[0] > distances[1]:
return p1
if distances[1] > distances[0]:
return p2

# same crowding distance: random

# use weaker dominance definition
# eval on common blocks only
common_blocks = blocks1 & blocks2
if common_blocks:
self.task.set_block_idx(list(sorted(common_blocks)))
res = self.task.evaluate([p1, p2], self.predictor)
vecs = self._get_objective_vectors(res)

if self._is_weakly_dominated(vecs[0], vecs[1]):
return p2
if self._is_weakly_dominated(vecs[1], vecs[0]):
return p1

return random.choice((p1, p2))


def _pick_incumbent_by_crowding(self, p1: Prompt, p2: Prompt) -> Prompt:
"""Break incumbent ties using crowding distance over common evaluated blocks."""
res = self.task.evaluate(self.incumbents, self.predictor, eval_strategy="evaluated")
common_blocks = self._get_common_blocks([p1, p2])
if common_blocks:
self.task.set_block_idx(common_blocks)
res = self.task.evaluate(self.incumbents, self.predictor)
inc_vectors = self._get_objective_vectors(res)
inc_distances = self._calculate_crowding_distance(inc_vectors)

Expand Down Expand Up @@ -430,6 +477,11 @@ def _non_dominated_sort(obj_vectors: np.ndarray) -> List[List[int]]:
def _is_dominated(vec1, vec2):
"""Returns True if vec2 dominates vec1 in a maximize-all setting."""
return np.all(vec2 >= vec1) and np.any(vec2 > vec1)

@staticmethod
def _is_weakly_dominated(vec1, vec2):
"""Returns True if vec2 weakly dominates vec1 in a maximize-all setting."""
return np.all(vec2 >= vec1)

@staticmethod
def _calculate_crowding_distance(obj_vectors: np.ndarray) -> np.ndarray:
Expand Down
Loading