Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/hf_steered.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/ibm_watsonx_ai.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/mamba_lm.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/nemo_lm.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/openai_completions.cpython-310.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/__pycache__/sglang_causallms.cpython-311.pyc +0 -0
- lm-evaluation-harness/lm_eval/models/hf_steered.py +243 -0
- lm-evaluation-harness/lm_eval/models/openai_completions.py +296 -0
- lm-evaluation-harness/lm_eval/models/utils.py +854 -0
- lm-evaluation-harness/lm_eval/models/vllm_vlms.py +323 -0
- lm-evaluation-harness/lm_eval/tasks/aclue/README.md +50 -0
- lm-evaluation-harness/lm_eval/tasks/aclue/aclue_named_entity_recognition.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/aclue/aclue_poetry_context_prediction.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/aclue/aclue_reading_comprehension.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/aclue/aclue_sentence_segmentation.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/README.md +130 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/_boolq_cot_2shot_yaml +32 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/app.yaml +12 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/just.yaml +13 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/land.yaml +13 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/val.yaml +12 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/_gen_yaml_2shot +25 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_grammar.lark +23 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_utils.py +1128 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/act_reach.yaml +19 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/app.yaml +19 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/just.yaml +20 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/land.yaml +19 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/_gen_yaml_2shot +26 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/act_reach.yaml +23 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/land.yaml +23 -0
- lm-evaluation-harness/lm_eval/tasks/acpbench/mcq_cot_2shot/just.yaml +12 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_yaml +33 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_zul.yaml +7 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sna.yaml +7 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sot.yaml +7 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_twi.yaml +6 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_vai.yaml +6 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yaml +33 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yor.yaml +6 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_zul.yaml +6 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lin.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lug.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_swa.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_twi.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_vai.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_wol.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_xho.yaml +4 -0
- lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_yaml +37 -0
lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-310.pyc
ADDED
|
Binary file (4.13 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/hf_steered.cpython-311.pyc
ADDED
|
Binary file (12.5 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/ibm_watsonx_ai.cpython-311.pyc
ADDED
|
Binary file (24 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/mamba_lm.cpython-310.pyc
ADDED
|
Binary file (4.36 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/nemo_lm.cpython-311.pyc
ADDED
|
Binary file (24.7 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/openai_completions.cpython-310.pyc
ADDED
|
Binary file (8.35 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/__pycache__/sglang_causallms.cpython-311.pyc
ADDED
|
Binary file (22.9 kB). View file
|
|
|
lm-evaluation-harness/lm_eval/models/hf_steered.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from contextlib import contextmanager
|
| 2 |
+
from functools import partial
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import Any, Callable, Generator, Optional, Union
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from peft.peft_model import PeftModel
|
| 8 |
+
from torch import Tensor, nn
|
| 9 |
+
from transformers import PreTrainedModel
|
| 10 |
+
|
| 11 |
+
from lm_eval.api.registry import register_model
|
| 12 |
+
from lm_eval.models.huggingface import HFLM
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@contextmanager
|
| 16 |
+
def steer(
|
| 17 |
+
model: Union[PreTrainedModel, PeftModel], hook_to_steer: dict[str, Callable]
|
| 18 |
+
) -> Generator[None, Any, None]:
|
| 19 |
+
"""
|
| 20 |
+
Context manager that temporarily hooks models and steers them.
|
| 21 |
+
|
| 22 |
+
Args:
|
| 23 |
+
model: The transformer model to hook
|
| 24 |
+
hook_to_steer: Dictionary mapping hookpoints to steering functions
|
| 25 |
+
|
| 26 |
+
Yields:
|
| 27 |
+
None
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def create_hook(hookpoint: str):
|
| 31 |
+
def hook_fn(module: nn.Module, input: Any, output: Tensor):
|
| 32 |
+
# If output is a tuple (like in some transformer layers), take first element
|
| 33 |
+
if isinstance(output, tuple):
|
| 34 |
+
output = (hook_to_steer[hookpoint](output[0]), *output[1:]) # type: ignore
|
| 35 |
+
else:
|
| 36 |
+
output = hook_to_steer[hookpoint](output)
|
| 37 |
+
|
| 38 |
+
return output
|
| 39 |
+
|
| 40 |
+
return hook_fn
|
| 41 |
+
|
| 42 |
+
handles = []
|
| 43 |
+
hookpoints = list(hook_to_steer.keys())
|
| 44 |
+
|
| 45 |
+
for name, module in model.base_model.named_modules():
|
| 46 |
+
if name in hookpoints:
|
| 47 |
+
handle = module.register_forward_hook(create_hook(name))
|
| 48 |
+
handles.append(handle)
|
| 49 |
+
|
| 50 |
+
if len(handles) != len(hookpoints):
|
| 51 |
+
raise ValueError(f"Not all hookpoints could be resolved: {hookpoints}")
|
| 52 |
+
|
| 53 |
+
try:
|
| 54 |
+
yield None
|
| 55 |
+
finally:
|
| 56 |
+
for handle in handles:
|
| 57 |
+
handle.remove()
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@register_model("steered")
|
| 61 |
+
class SteeredModel(HFLM):
|
| 62 |
+
hook_to_steer: dict[str, Callable]
|
| 63 |
+
|
| 64 |
+
def __init__(
|
| 65 |
+
self,
|
| 66 |
+
pretrained: str,
|
| 67 |
+
steer_path: str,
|
| 68 |
+
device: Optional[str] = None,
|
| 69 |
+
**kwargs,
|
| 70 |
+
):
|
| 71 |
+
"""
|
| 72 |
+
HFLM with a steered forward pass.
|
| 73 |
+
|
| 74 |
+
To derive steering vectors from a sparse model loadable with sparsify or sae_lens,
|
| 75 |
+
provide the path to a CSV file with the following columns (example rows are provided below):
|
| 76 |
+
|
| 77 |
+
loader,action,sparse_model,hookpoint,feature_index,steering_coefficient,sae_id,description,
|
| 78 |
+
sparsify,add,EleutherAI/sae-pythia-70m-32k,layers.3,30,10.0,,,
|
| 79 |
+
sae_lens,add,gemma-scope-2b-pt-res-canonical,layers.20,12082,240.0,layer_20/width_16k/canonical,increase dogs,
|
| 80 |
+
|
| 81 |
+
To load steering vectors directly, provide the path to a pytorch (.pt) file with content in the following format:
|
| 82 |
+
|
| 83 |
+
{
|
| 84 |
+
hookpoint: {
|
| 85 |
+
"steering_vector": <torch.Tensor>,
|
| 86 |
+
"steering_coefficient": <float>,
|
| 87 |
+
"action": <Literal["add", "clamp"]>,
|
| 88 |
+
"bias": <torch.Tensor | None>,
|
| 89 |
+
},
|
| 90 |
+
...
|
| 91 |
+
}
|
| 92 |
+
"""
|
| 93 |
+
super().__init__(pretrained=pretrained, device=device, **kwargs)
|
| 94 |
+
|
| 95 |
+
if steer_path.endswith(".pt") or steer_path.endswith(".pth"):
|
| 96 |
+
with open(steer_path, "rb") as f:
|
| 97 |
+
steer_config: dict[str, dict[str, Any]] = torch.load(
|
| 98 |
+
f, weights_only=True
|
| 99 |
+
)
|
| 100 |
+
elif steer_path.endswith(".csv"):
|
| 101 |
+
steer_config = self.derive_steer_config(steer_path)
|
| 102 |
+
else:
|
| 103 |
+
raise ValueError(f"Unknown steer file type: {steer_path}")
|
| 104 |
+
|
| 105 |
+
hook_to_steer = {}
|
| 106 |
+
for hookpoint, steer_info in steer_config.items():
|
| 107 |
+
action = steer_info["action"]
|
| 108 |
+
steering_coefficient = steer_info["steering_coefficient"]
|
| 109 |
+
steering_vector = (
|
| 110 |
+
steer_info["steering_vector"].to(self.device).to(self.model.dtype)
|
| 111 |
+
)
|
| 112 |
+
bias = (
|
| 113 |
+
steer_info["bias"].to(self.device).to(self.model.dtype)
|
| 114 |
+
if steer_info["bias"] is not None
|
| 115 |
+
else None
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
if action == "add":
|
| 119 |
+
# Steers the model by adding some multiple of a steering vector to all sequence positions.
|
| 120 |
+
hook_to_steer[hookpoint] = (
|
| 121 |
+
lambda acts: acts + steering_coefficient * steering_vector
|
| 122 |
+
)
|
| 123 |
+
elif action == "clamp":
|
| 124 |
+
hook_to_steer[hookpoint] = partial(
|
| 125 |
+
self.clamp,
|
| 126 |
+
steering_vector=steering_vector,
|
| 127 |
+
value=steering_coefficient,
|
| 128 |
+
bias=bias,
|
| 129 |
+
)
|
| 130 |
+
else:
|
| 131 |
+
raise ValueError(f"Unknown hook type: {action}")
|
| 132 |
+
|
| 133 |
+
self.hook_to_steer = hook_to_steer
|
| 134 |
+
|
| 135 |
+
@classmethod
|
| 136 |
+
def derive_steer_config(cls, steer_path: str):
|
| 137 |
+
"""Derive a dictionary of steering vectors from sparse model(/s) specified in a CSV file."""
|
| 138 |
+
import pandas as pd
|
| 139 |
+
|
| 140 |
+
df = pd.read_csv(steer_path)
|
| 141 |
+
steer_data: dict[str, dict[str, Any]] = {}
|
| 142 |
+
|
| 143 |
+
if any(df["loader"] == "sparsify"):
|
| 144 |
+
from sparsify import SparseCoder
|
| 145 |
+
if any(df["loader"] == "sae_lens"):
|
| 146 |
+
from sae_lens import SAE
|
| 147 |
+
|
| 148 |
+
sae_cache = {}
|
| 149 |
+
|
| 150 |
+
def load_from_sae_lens(sae_release: str, sae_id: str):
|
| 151 |
+
cache_key = (sae_release, sae_id)
|
| 152 |
+
if cache_key not in sae_cache:
|
| 153 |
+
sae_cache[cache_key] = SAE.from_pretrained(sae_release, sae_id)[0]
|
| 154 |
+
|
| 155 |
+
return sae_cache[cache_key]
|
| 156 |
+
|
| 157 |
+
for _, row in df.iterrows():
|
| 158 |
+
action = row.get("action", "add")
|
| 159 |
+
sparse_name = row["sparse_model"]
|
| 160 |
+
hookpoint = row["hookpoint"]
|
| 161 |
+
feature_index = int(row["feature_index"])
|
| 162 |
+
steering_coefficient = float(row["steering_coefficient"])
|
| 163 |
+
loader = row.get("loader", "sparsify")
|
| 164 |
+
|
| 165 |
+
if loader == "sparsify":
|
| 166 |
+
name_path = Path(sparse_name)
|
| 167 |
+
|
| 168 |
+
sparse_coder = (
|
| 169 |
+
SparseCoder.load_from_disk(name_path / hookpoint)
|
| 170 |
+
if name_path.exists()
|
| 171 |
+
else SparseCoder.load_from_hub(sparse_name, hookpoint)
|
| 172 |
+
)
|
| 173 |
+
assert sparse_coder.W_dec is not None
|
| 174 |
+
|
| 175 |
+
steering_vector = sparse_coder.W_dec[feature_index]
|
| 176 |
+
bias = sparse_coder.b_dec
|
| 177 |
+
|
| 178 |
+
elif loader == "sae_lens":
|
| 179 |
+
sparse_coder = load_from_sae_lens(
|
| 180 |
+
sae_release=sparse_name, sae_id=row["sae_id"]
|
| 181 |
+
)
|
| 182 |
+
steering_vector = sparse_coder.W_dec[feature_index]
|
| 183 |
+
bias = sparse_coder.b_dec
|
| 184 |
+
if hookpoint == "" or pd.isna(hookpoint):
|
| 185 |
+
hookpoint = sparse_coder.cfg.hook_name
|
| 186 |
+
else:
|
| 187 |
+
raise ValueError(f"Unknown loader: {loader}")
|
| 188 |
+
|
| 189 |
+
steer_data[hookpoint] = {
|
| 190 |
+
"action": action,
|
| 191 |
+
"steering_coefficient": steering_coefficient,
|
| 192 |
+
"steering_vector": steering_vector,
|
| 193 |
+
"bias": bias,
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
return steer_data
|
| 197 |
+
|
| 198 |
+
@classmethod
|
| 199 |
+
def clamp(
|
| 200 |
+
cls,
|
| 201 |
+
acts: Tensor,
|
| 202 |
+
steering_vector: Tensor,
|
| 203 |
+
value: float,
|
| 204 |
+
bias: Optional[Tensor] = None,
|
| 205 |
+
):
|
| 206 |
+
"""Clamps a direction of the activations to be the steering vector * the value.
|
| 207 |
+
|
| 208 |
+
Args:
|
| 209 |
+
acts (Tensor): The activations tensor to edit of shape [batch, pos, features]
|
| 210 |
+
steering_vector (Tensor): A direction to clamp of shape [features]
|
| 211 |
+
value (float): Value to clamp the direction to
|
| 212 |
+
bias (Tensor | None): Optional bias to add to the activations
|
| 213 |
+
|
| 214 |
+
Returns:
|
| 215 |
+
Tensor: The modified activations with the specified direction clamped
|
| 216 |
+
"""
|
| 217 |
+
|
| 218 |
+
if bias is not None:
|
| 219 |
+
acts = acts - bias
|
| 220 |
+
|
| 221 |
+
direction = steering_vector / torch.norm(steering_vector)
|
| 222 |
+
proj_magnitude = torch.sum(acts * direction, dim=-1, keepdim=True)
|
| 223 |
+
orthogonal_component = acts - proj_magnitude * direction
|
| 224 |
+
|
| 225 |
+
clamped = orthogonal_component + direction * value
|
| 226 |
+
|
| 227 |
+
if bias is not None:
|
| 228 |
+
return clamped + bias
|
| 229 |
+
|
| 230 |
+
return clamped
|
| 231 |
+
|
| 232 |
+
def forward(self, *args, **kwargs):
|
| 233 |
+
with torch.no_grad():
|
| 234 |
+
with steer(self.model, self.hook_to_steer):
|
| 235 |
+
return self.model.forward(*args, **kwargs)
|
| 236 |
+
|
| 237 |
+
def _model_call(self, *args, **kwargs):
|
| 238 |
+
with steer(self.model, self.hook_to_steer):
|
| 239 |
+
return super()._model_call(*args, **kwargs)
|
| 240 |
+
|
| 241 |
+
def _model_generate(self, *args, **kwargs):
|
| 242 |
+
with steer(self.model, self.hook_to_steer):
|
| 243 |
+
return super()._model_generate(*args, **kwargs)
|
lm-evaluation-harness/lm_eval/models/openai_completions.py
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import os
|
| 3 |
+
from functools import cached_property
|
| 4 |
+
from operator import itemgetter
|
| 5 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 6 |
+
|
| 7 |
+
from lm_eval.api.registry import register_model
|
| 8 |
+
from lm_eval.models.api_models import TemplateAPI
|
| 9 |
+
from lm_eval.models.utils import handle_stop_sequences
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
eval_logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@register_model("local-completions")
|
| 16 |
+
class LocalCompletionsAPI(TemplateAPI):
|
| 17 |
+
def __init__(
|
| 18 |
+
self,
|
| 19 |
+
base_url=None,
|
| 20 |
+
tokenizer_backend="huggingface",
|
| 21 |
+
**kwargs,
|
| 22 |
+
):
|
| 23 |
+
super().__init__(
|
| 24 |
+
base_url=base_url, tokenizer_backend=tokenizer_backend, **kwargs
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
def _create_payload(
|
| 28 |
+
self,
|
| 29 |
+
messages: Union[List[List[int]], List[dict], List[str], str],
|
| 30 |
+
generate=False,
|
| 31 |
+
gen_kwargs: Optional[dict] = None,
|
| 32 |
+
seed: int = 1234,
|
| 33 |
+
eos=None,
|
| 34 |
+
**kwargs,
|
| 35 |
+
) -> dict:
|
| 36 |
+
if generate:
|
| 37 |
+
gen_kwargs.pop("do_sample", False)
|
| 38 |
+
if "max_tokens" in gen_kwargs:
|
| 39 |
+
max_tokens = gen_kwargs.pop("max_tokens")
|
| 40 |
+
else:
|
| 41 |
+
max_tokens = gen_kwargs.pop("max_gen_toks", self._max_gen_toks)
|
| 42 |
+
temperature = gen_kwargs.pop("temperature", 0)
|
| 43 |
+
stop = handle_stop_sequences(gen_kwargs.pop("until", None), eos)
|
| 44 |
+
return {
|
| 45 |
+
"prompt": messages,
|
| 46 |
+
"model": self.model,
|
| 47 |
+
"max_tokens": max_tokens,
|
| 48 |
+
"temperature": temperature,
|
| 49 |
+
"stop": stop,
|
| 50 |
+
"seed": seed,
|
| 51 |
+
**gen_kwargs,
|
| 52 |
+
}
|
| 53 |
+
else:
|
| 54 |
+
return {
|
| 55 |
+
"model": self.model,
|
| 56 |
+
"prompt": messages,
|
| 57 |
+
"temperature": 0,
|
| 58 |
+
"max_tokens": 1,
|
| 59 |
+
"logprobs": 1,
|
| 60 |
+
"seed": seed,
|
| 61 |
+
"echo": True,
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
@staticmethod
|
| 65 |
+
def parse_logprobs(
|
| 66 |
+
outputs: Union[Dict, List[Dict]],
|
| 67 |
+
tokens: List[List[int]] = None,
|
| 68 |
+
ctxlens: List[int] = None,
|
| 69 |
+
**kwargs,
|
| 70 |
+
) -> List[Tuple[float, bool]]:
|
| 71 |
+
res = []
|
| 72 |
+
if not isinstance(outputs, list):
|
| 73 |
+
outputs = [outputs]
|
| 74 |
+
for out in outputs:
|
| 75 |
+
for choice, ctxlen in zip(
|
| 76 |
+
sorted(out["choices"], key=itemgetter("index")), ctxlens
|
| 77 |
+
):
|
| 78 |
+
assert ctxlen > 0, "Context length must be greater than 0"
|
| 79 |
+
logprobs = sum(choice["logprobs"]["token_logprobs"][ctxlen:-1])
|
| 80 |
+
tokens_logprobs = choice["logprobs"]["token_logprobs"][ctxlen:-1]
|
| 81 |
+
top_logprobs = choice["logprobs"]["top_logprobs"][ctxlen:-1]
|
| 82 |
+
is_greedy = True
|
| 83 |
+
for tok, top in zip(tokens_logprobs, top_logprobs):
|
| 84 |
+
if tok != max(top.values()):
|
| 85 |
+
is_greedy = False
|
| 86 |
+
break
|
| 87 |
+
res.append((logprobs, is_greedy))
|
| 88 |
+
return res
|
| 89 |
+
|
| 90 |
+
@staticmethod
|
| 91 |
+
def parse_generations(outputs: Union[Dict, List[Dict]], **kwargs) -> List[str]:
|
| 92 |
+
res = []
|
| 93 |
+
if not isinstance(outputs, list):
|
| 94 |
+
outputs = [outputs]
|
| 95 |
+
for out in outputs:
|
| 96 |
+
tmp = [None] * len(out["choices"])
|
| 97 |
+
for choices in out["choices"]:
|
| 98 |
+
tmp[choices["index"]] = choices["text"]
|
| 99 |
+
res = res + tmp
|
| 100 |
+
return res
|
| 101 |
+
|
| 102 |
+
@property
|
| 103 |
+
def api_key(self):
|
| 104 |
+
return os.environ.get("OPENAI_API_KEY", "")
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
@register_model("local-chat-completions")
|
| 108 |
+
class LocalChatCompletion(LocalCompletionsAPI):
|
| 109 |
+
def __init__(
|
| 110 |
+
self,
|
| 111 |
+
base_url=None,
|
| 112 |
+
tokenizer_backend=None,
|
| 113 |
+
tokenized_requests=False,
|
| 114 |
+
**kwargs,
|
| 115 |
+
):
|
| 116 |
+
eval_logger.warning(
|
| 117 |
+
"chat-completions endpoint requires the `--apply_chat_template` flag."
|
| 118 |
+
)
|
| 119 |
+
super().__init__(
|
| 120 |
+
base_url=base_url,
|
| 121 |
+
tokenizer_backend=tokenizer_backend,
|
| 122 |
+
tokenized_requests=tokenized_requests,
|
| 123 |
+
**kwargs,
|
| 124 |
+
)
|
| 125 |
+
if self._batch_size > 1:
|
| 126 |
+
eval_logger.warning(
|
| 127 |
+
"Chat completions does not support batching. Defaulting to batch size 1."
|
| 128 |
+
)
|
| 129 |
+
self._batch_size = 1
|
| 130 |
+
|
| 131 |
+
def _create_payload(
|
| 132 |
+
self,
|
| 133 |
+
messages: List[Dict],
|
| 134 |
+
generate=False,
|
| 135 |
+
gen_kwargs: dict = None,
|
| 136 |
+
seed=1234,
|
| 137 |
+
eos=None,
|
| 138 |
+
**kwargs,
|
| 139 |
+
) -> dict:
|
| 140 |
+
assert type(messages) is not str, (
|
| 141 |
+
"chat-completions require the --apply_chat_template flag."
|
| 142 |
+
)
|
| 143 |
+
gen_kwargs.pop("do_sample", False)
|
| 144 |
+
if "max_tokens" in gen_kwargs:
|
| 145 |
+
max_tokens = gen_kwargs.pop("max_tokens")
|
| 146 |
+
else:
|
| 147 |
+
max_tokens = gen_kwargs.pop("max_gen_toks", self._max_gen_toks)
|
| 148 |
+
temperature = gen_kwargs.pop("temperature", 0)
|
| 149 |
+
stop = handle_stop_sequences(gen_kwargs.pop("until", None), eos)
|
| 150 |
+
if not isinstance(stop, (list, tuple)):
|
| 151 |
+
stop = [stop]
|
| 152 |
+
return {
|
| 153 |
+
"messages": messages,
|
| 154 |
+
"model": self.model,
|
| 155 |
+
"max_tokens": max_tokens,
|
| 156 |
+
"temperature": temperature,
|
| 157 |
+
"stop": stop[:4],
|
| 158 |
+
"seed": seed,
|
| 159 |
+
**gen_kwargs,
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
@staticmethod
|
| 163 |
+
def parse_generations(outputs: Union[Dict, List[Dict]], **kwargs) -> List[str]:
|
| 164 |
+
res = []
|
| 165 |
+
if not isinstance(outputs, list):
|
| 166 |
+
outputs = [outputs]
|
| 167 |
+
for out in outputs:
|
| 168 |
+
tmp = [None] * len(out["choices"])
|
| 169 |
+
for choices in out["choices"]:
|
| 170 |
+
tmp[choices["index"]] = choices["message"]["content"]
|
| 171 |
+
res = res + tmp
|
| 172 |
+
return res
|
| 173 |
+
|
| 174 |
+
def tok_encode(
|
| 175 |
+
self,
|
| 176 |
+
string: Union[str, Any],
|
| 177 |
+
left_truncate_len=None,
|
| 178 |
+
add_special_tokens=None,
|
| 179 |
+
**kwargs,
|
| 180 |
+
) -> Union[List[str], List[int], Any]:
|
| 181 |
+
return string
|
| 182 |
+
|
| 183 |
+
def loglikelihood(self, requests, **kwargs):
|
| 184 |
+
raise NotImplementedError(
|
| 185 |
+
"Loglikelihood is not supported for chat completions. Consider using the completions API instead."
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
@register_model(
|
| 190 |
+
"openai-completions",
|
| 191 |
+
)
|
| 192 |
+
class OpenAICompletionsAPI(LocalCompletionsAPI):
|
| 193 |
+
def __init__(
|
| 194 |
+
self,
|
| 195 |
+
base_url="https://api.openai.com/v1/completions",
|
| 196 |
+
tokenizer_backend="tiktoken",
|
| 197 |
+
**kwargs,
|
| 198 |
+
):
|
| 199 |
+
super().__init__(
|
| 200 |
+
base_url=base_url, tokenizer_backend=tokenizer_backend, **kwargs
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
@cached_property
|
| 204 |
+
def api_key(self):
|
| 205 |
+
"""Override this property to return the API key for the API request."""
|
| 206 |
+
key = os.environ.get("OPENAI_API_KEY", None)
|
| 207 |
+
if key is None:
|
| 208 |
+
raise ValueError(
|
| 209 |
+
"API key not found. Please set the `OPENAI_API_KEY` environment variable."
|
| 210 |
+
)
|
| 211 |
+
return key
|
| 212 |
+
|
| 213 |
+
def loglikelihood(self, requests, **kwargs):
|
| 214 |
+
assert self.model in [
|
| 215 |
+
"babbage-002",
|
| 216 |
+
"davinci-002",
|
| 217 |
+
], (
|
| 218 |
+
f"Prompt loglikelihoods are only supported by OpenAI's API for {['babbage-002', 'davinci-002']}."
|
| 219 |
+
)
|
| 220 |
+
return super().loglikelihood(requests, **kwargs)
|
| 221 |
+
|
| 222 |
+
def chat_template(self, chat_template: Union[bool, str] = False) -> Optional[str]:
|
| 223 |
+
return ""
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
@register_model("openai-chat-completions")
|
| 227 |
+
class OpenAIChatCompletion(LocalChatCompletion):
|
| 228 |
+
def __init__(
|
| 229 |
+
self,
|
| 230 |
+
base_url="https://api.openai.com/v1/chat/completions",
|
| 231 |
+
tokenizer_backend=None,
|
| 232 |
+
tokenized_requests=False,
|
| 233 |
+
**kwargs,
|
| 234 |
+
):
|
| 235 |
+
if "o1" in kwargs.get("model", ""):
|
| 236 |
+
eval_logger.warning(
|
| 237 |
+
"o1 models do not support `stop` and only support temperature=1"
|
| 238 |
+
)
|
| 239 |
+
super().__init__(
|
| 240 |
+
base_url=base_url,
|
| 241 |
+
tokenizer_backend=tokenizer_backend,
|
| 242 |
+
tokenized_requests=tokenized_requests,
|
| 243 |
+
**kwargs,
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
@cached_property
|
| 247 |
+
def api_key(self):
|
| 248 |
+
"""Override this property to return the API key for the API request."""
|
| 249 |
+
key = os.environ.get("OPENAI_API_KEY", None)
|
| 250 |
+
if key is None:
|
| 251 |
+
raise ValueError(
|
| 252 |
+
"API key not found. Please set the `OPENAI_API_KEY` environment variable."
|
| 253 |
+
)
|
| 254 |
+
return key
|
| 255 |
+
|
| 256 |
+
def loglikelihood(self, requests, **kwargs):
|
| 257 |
+
raise NotImplementedError(
|
| 258 |
+
"Loglikelihood (and therefore `multiple_choice`-type tasks) is not supported for chat completions as OpenAI does not provide prompt logprobs. See https://github.com/EleutherAI/lm-evaluation-harness/issues/942#issuecomment-1777836312 or https://github.com/EleutherAI/lm-evaluation-harness/issues/1196 for more background on this limitation."
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
def _create_payload(
|
| 262 |
+
self,
|
| 263 |
+
messages: List[Dict],
|
| 264 |
+
generate=False,
|
| 265 |
+
gen_kwargs: dict = None,
|
| 266 |
+
seed=1234,
|
| 267 |
+
eos="<|endoftext|>",
|
| 268 |
+
**kwargs,
|
| 269 |
+
) -> dict:
|
| 270 |
+
assert type(messages) is not str, (
|
| 271 |
+
"chat-completions require the --apply_chat_template flag."
|
| 272 |
+
)
|
| 273 |
+
gen_kwargs.pop("do_sample", False)
|
| 274 |
+
if "max_tokens" in gen_kwargs:
|
| 275 |
+
max_tokens = gen_kwargs.pop("max_tokens")
|
| 276 |
+
else:
|
| 277 |
+
max_tokens = gen_kwargs.pop("max_gen_toks", self._max_gen_toks)
|
| 278 |
+
temperature = gen_kwargs.pop("temperature", 0)
|
| 279 |
+
stop = handle_stop_sequences(gen_kwargs.pop("until", ["<|endoftext|>"]), eos)
|
| 280 |
+
if not isinstance(stop, (list, tuple)):
|
| 281 |
+
stop = [stop]
|
| 282 |
+
output = {
|
| 283 |
+
"messages": messages,
|
| 284 |
+
"model": self.model,
|
| 285 |
+
"max_completion_tokens": max_tokens,
|
| 286 |
+
"temperature": temperature,
|
| 287 |
+
"stop": stop[:4],
|
| 288 |
+
"seed": seed,
|
| 289 |
+
**gen_kwargs,
|
| 290 |
+
}
|
| 291 |
+
if "o1" in self.model:
|
| 292 |
+
output.pop("stop")
|
| 293 |
+
output["temperature"] = 1
|
| 294 |
+
elif "o3" in self.model:
|
| 295 |
+
output.pop("temperature")
|
| 296 |
+
return output
|
lm-evaluation-harness/lm_eval/models/utils.py
ADDED
|
@@ -0,0 +1,854 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import collections
|
| 2 |
+
import fnmatch
|
| 3 |
+
import gc
|
| 4 |
+
import itertools
|
| 5 |
+
import logging
|
| 6 |
+
import time
|
| 7 |
+
from functools import wraps
|
| 8 |
+
from typing import (
|
| 9 |
+
TYPE_CHECKING,
|
| 10 |
+
Any,
|
| 11 |
+
Callable,
|
| 12 |
+
Dict,
|
| 13 |
+
Iterable,
|
| 14 |
+
Iterator,
|
| 15 |
+
List,
|
| 16 |
+
Literal,
|
| 17 |
+
Optional,
|
| 18 |
+
Tuple,
|
| 19 |
+
Type,
|
| 20 |
+
Union,
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
import transformers
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
eval_logger = logging.getLogger(__name__)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
if TYPE_CHECKING:
|
| 31 |
+
from PIL import Image
|
| 32 |
+
from transformers import PreTrainedTokenizerBase
|
| 33 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def chunks(iter, n: int = 0, fn=None):
|
| 37 |
+
"""
|
| 38 |
+
Divides an iterable into chunks of specified size or based on a given function.
|
| 39 |
+
Useful for batching
|
| 40 |
+
|
| 41 |
+
Parameters:
|
| 42 |
+
- iter: The input iterable to be divided into chunks.
|
| 43 |
+
- n: An integer representing the size of each chunk. Default is 0.
|
| 44 |
+
- fn: A function that takes the current index and the iterable as arguments and returns the size of the chunk. Default is None.
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
An iterator that yields chunks of the input iterable.
|
| 48 |
+
|
| 49 |
+
Example usage:
|
| 50 |
+
```
|
| 51 |
+
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
| 52 |
+
for chunk in chunks(data, 3):
|
| 53 |
+
print(chunk)
|
| 54 |
+
```
|
| 55 |
+
Output:
|
| 56 |
+
```
|
| 57 |
+
[1, 2, 3]
|
| 58 |
+
[4, 5, 6]
|
| 59 |
+
[7, 8, 9]
|
| 60 |
+
[10]
|
| 61 |
+
```
|
| 62 |
+
"""
|
| 63 |
+
arr = []
|
| 64 |
+
for i, x in enumerate(iter):
|
| 65 |
+
arr.append(x)
|
| 66 |
+
if len(arr) == (fn(i, iter) if fn else n):
|
| 67 |
+
yield arr
|
| 68 |
+
arr = []
|
| 69 |
+
|
| 70 |
+
if arr:
|
| 71 |
+
yield arr
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class MultiChoice:
|
| 75 |
+
def __init__(self, choices) -> None:
|
| 76 |
+
self.choices = choices
|
| 77 |
+
|
| 78 |
+
# Simple wildcard support (linux filename patterns)
|
| 79 |
+
def __contains__(self, values) -> bool:
|
| 80 |
+
for value in values.split(","):
|
| 81 |
+
if len(fnmatch.filter(self.choices, value)) == 0:
|
| 82 |
+
eval_logger.info("Available tasks to choose:")
|
| 83 |
+
for choice in self.choices:
|
| 84 |
+
eval_logger.info(f" - {choice}")
|
| 85 |
+
raise ValueError("'{}' is not in task list".format(value))
|
| 86 |
+
return True
|
| 87 |
+
|
| 88 |
+
def __iter__(self) -> Iterator:
|
| 89 |
+
for choice in self.choices:
|
| 90 |
+
yield choice
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class Grouper:
|
| 94 |
+
"""
|
| 95 |
+
takes an array `arr` and function `fn` and returns a dictionary
|
| 96 |
+
with keys fn(ob) for each ob in `arr` and with values `self.arr[key]` a list of all
|
| 97 |
+
objects in `arr` satisfying `key == fn(ob)`.
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
def __init__(self, arr, fn) -> None:
|
| 101 |
+
# self.orig_arr = arr
|
| 102 |
+
self.size = len(arr)
|
| 103 |
+
arr = list(enumerate(arr))
|
| 104 |
+
|
| 105 |
+
def group_return_dict(arr, fn):
|
| 106 |
+
res = collections.defaultdict(list)
|
| 107 |
+
|
| 108 |
+
for ob in arr:
|
| 109 |
+
res[fn(ob)].append(ob)
|
| 110 |
+
return res
|
| 111 |
+
|
| 112 |
+
arr = group_return_dict(arr, lambda x: fn(x[1]))
|
| 113 |
+
|
| 114 |
+
# self.arr has format Dict[Tuple[int, <entry from orig. arr>]]
|
| 115 |
+
self.arr = arr
|
| 116 |
+
self._grouped = None
|
| 117 |
+
|
| 118 |
+
def get_grouped(self):
|
| 119 |
+
# return the contents but not indices for our grouped dict.
|
| 120 |
+
if self._grouped:
|
| 121 |
+
return self._grouped
|
| 122 |
+
grouped = {}
|
| 123 |
+
for key in self.arr.keys():
|
| 124 |
+
# drop the index from each element of self.arr
|
| 125 |
+
grouped[key] = [y[1] for y in self.arr[key]]
|
| 126 |
+
self._grouped = grouped
|
| 127 |
+
return grouped
|
| 128 |
+
|
| 129 |
+
def get_original(self, grouped_dict):
|
| 130 |
+
# take in a grouped dictionary with e.g. results for each key listed
|
| 131 |
+
# in the same order as the instances in `self.arr`, and
|
| 132 |
+
# return the results in the same (single list) order as `self.orig_arr`.
|
| 133 |
+
res = [None] * self.size
|
| 134 |
+
cov = [False] * self.size
|
| 135 |
+
# orig = [None] * self.size
|
| 136 |
+
|
| 137 |
+
assert grouped_dict.keys() == self.arr.keys()
|
| 138 |
+
|
| 139 |
+
for key in grouped_dict.keys():
|
| 140 |
+
for (ind, _), v in zip(self.arr[key], grouped_dict[key]):
|
| 141 |
+
res[ind] = v
|
| 142 |
+
cov[ind] = True
|
| 143 |
+
# orig[ind] = _
|
| 144 |
+
|
| 145 |
+
assert all(cov)
|
| 146 |
+
# assert orig == self.orig_arr
|
| 147 |
+
|
| 148 |
+
return res
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def pad_and_concat(
|
| 152 |
+
max_length: int,
|
| 153 |
+
tensors: List[torch.Tensor],
|
| 154 |
+
padding_side: Literal["right", "left"] = "right",
|
| 155 |
+
):
|
| 156 |
+
"""
|
| 157 |
+
Method for padding a list of tensors given the maximum tensor
|
| 158 |
+
length in the batch. Used for batching inputs and continuations in
|
| 159 |
+
seq2seq models.
|
| 160 |
+
"""
|
| 161 |
+
assert padding_side == "left" or padding_side == "right", (
|
| 162 |
+
f"Unrecognized padding type: '{padding_side}' not 'left' or 'right'"
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
for i, tensor in enumerate(tensors):
|
| 166 |
+
if len(tensor.shape) == 2:
|
| 167 |
+
tensor = tensor.squeeze(0) # squeeze, in case passed [1, seq] size
|
| 168 |
+
tensor_len = tensor.shape[0]
|
| 169 |
+
if tensor_len < max_length:
|
| 170 |
+
if padding_side == "right":
|
| 171 |
+
# right-pad
|
| 172 |
+
tensors[i] = torch.cat(
|
| 173 |
+
[
|
| 174 |
+
tensor, # [seq]
|
| 175 |
+
torch.zeros(
|
| 176 |
+
max_length - tensor_len,
|
| 177 |
+
dtype=torch.long,
|
| 178 |
+
device=tensor.device,
|
| 179 |
+
), # [padding_length - seq]
|
| 180 |
+
],
|
| 181 |
+
dim=0,
|
| 182 |
+
).unsqueeze(0)
|
| 183 |
+
else:
|
| 184 |
+
# left-pad
|
| 185 |
+
tensors[i] = torch.cat(
|
| 186 |
+
[
|
| 187 |
+
torch.zeros(
|
| 188 |
+
max_length - tensor_len,
|
| 189 |
+
dtype=torch.long,
|
| 190 |
+
device=tensor.device,
|
| 191 |
+
), # [padding_length - seq]
|
| 192 |
+
tensor, # [seq]
|
| 193 |
+
],
|
| 194 |
+
dim=0,
|
| 195 |
+
).unsqueeze(0)
|
| 196 |
+
else:
|
| 197 |
+
tensors[i] = tensor.unsqueeze(0)
|
| 198 |
+
|
| 199 |
+
return torch.cat(tensors, dim=0)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def clear_torch_cache() -> None:
|
| 203 |
+
gc.collect()
|
| 204 |
+
torch.cuda.empty_cache()
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def get_dtype(dtype: Union[str, torch.dtype]) -> torch.dtype:
|
| 208 |
+
"""Converts `dtype` from `str` to torch.dtype when possible. Does not use an instantiated HF AutoConfig"""
|
| 209 |
+
if isinstance(dtype, str) and dtype != "auto":
|
| 210 |
+
# Convert `str` args torch dtype: `float16` -> `torch.float16`
|
| 211 |
+
_torch_dtype = getattr(torch, dtype)
|
| 212 |
+
else:
|
| 213 |
+
_torch_dtype = dtype
|
| 214 |
+
return _torch_dtype
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
class MultiTokenEOSCriteria(transformers.StoppingCriteria):
|
| 218 |
+
"""Criteria to stop on the specified multi-token sequence."""
|
| 219 |
+
|
| 220 |
+
def __init__(
|
| 221 |
+
self,
|
| 222 |
+
sequence: str,
|
| 223 |
+
tokenizer: transformers.PreTrainedTokenizer,
|
| 224 |
+
initial_decoder_input_length: int,
|
| 225 |
+
batch_size: int,
|
| 226 |
+
) -> None:
|
| 227 |
+
self.initial_decoder_input_length = initial_decoder_input_length
|
| 228 |
+
self.done_tracker = [False] * batch_size
|
| 229 |
+
self.sequence = sequence
|
| 230 |
+
self.sequence_ids = tokenizer.encode(sequence, add_special_tokens=False)
|
| 231 |
+
# print(sequence, self.sequence_ids)
|
| 232 |
+
# we look back for 2 more tokens than it takes to encode our stop sequence
|
| 233 |
+
# because tokenizers suck, and a model might generate `['\n', '\n']` but our `sequence` is `['\n\n']`
|
| 234 |
+
# and we don't want to mistakenly not stop a generation because our
|
| 235 |
+
# (string) stop sequence was output in a different tokenization
|
| 236 |
+
|
| 237 |
+
# NOTE: there is a minor danger that this will end up looking back 2 tokens into the past, into the inputs to the model,
|
| 238 |
+
# and stopping generation immediately as a result. With only 2 extra tokens of lookback, this risk is minimized
|
| 239 |
+
# Additionally, in lookback_ids_batch we should prevent ever looking back into the inputs as described.
|
| 240 |
+
self.sequence_id_len = len(self.sequence_ids) + 2
|
| 241 |
+
self.tokenizer = tokenizer
|
| 242 |
+
|
| 243 |
+
def __call__(self, input_ids, scores, **kwargs) -> bool:
|
| 244 |
+
# For efficiency, we compare the last n tokens where n is the number of tokens in the stop_sequence
|
| 245 |
+
lookback_ids_batch = input_ids[:, self.initial_decoder_input_length :]
|
| 246 |
+
|
| 247 |
+
lookback_ids_batch = lookback_ids_batch[:, -self.sequence_id_len :]
|
| 248 |
+
|
| 249 |
+
lookback_tokens_batch = self.tokenizer.batch_decode(lookback_ids_batch)
|
| 250 |
+
|
| 251 |
+
for i, done in enumerate(self.done_tracker):
|
| 252 |
+
if not done:
|
| 253 |
+
self.done_tracker[i] = self.sequence in lookback_tokens_batch[i]
|
| 254 |
+
return False not in self.done_tracker
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def stop_sequences_criteria(
|
| 258 |
+
tokenizer: transformers.PreTrainedTokenizer,
|
| 259 |
+
stop_sequences: List[str],
|
| 260 |
+
initial_decoder_input_length: int,
|
| 261 |
+
batch_size: int,
|
| 262 |
+
) -> transformers.StoppingCriteriaList:
|
| 263 |
+
return transformers.StoppingCriteriaList(
|
| 264 |
+
[
|
| 265 |
+
*[
|
| 266 |
+
MultiTokenEOSCriteria(
|
| 267 |
+
sequence, tokenizer, initial_decoder_input_length, batch_size
|
| 268 |
+
)
|
| 269 |
+
for sequence in stop_sequences
|
| 270 |
+
],
|
| 271 |
+
]
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def undistribute(iterable):
|
| 276 |
+
"""
|
| 277 |
+
Undoes https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute .
|
| 278 |
+
|
| 279 |
+
Re-interleaves results that have been split using more_itertools.distribute:
|
| 280 |
+
>>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6])
|
| 281 |
+
>>> list(group_1)
|
| 282 |
+
[1, 3, 5]
|
| 283 |
+
>>> list(group_2)
|
| 284 |
+
[2, 4, 6]
|
| 285 |
+
>>> undistribute([group_1, group_2])
|
| 286 |
+
[1, 2, 3, 4, 5, 6]
|
| 287 |
+
|
| 288 |
+
Handles non-uniform component lengths:
|
| 289 |
+
|
| 290 |
+
>>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7])
|
| 291 |
+
>>> [list(c) for c in children]
|
| 292 |
+
[[1, 4, 7], [2, 5], [3, 6]]
|
| 293 |
+
>>> undistribute(children)
|
| 294 |
+
[1, 2, 3, 4, 5, 6, 7]
|
| 295 |
+
|
| 296 |
+
Also handles when some iterables are empty:
|
| 297 |
+
|
| 298 |
+
>>> children = distribute(5, [1, 2, 3])
|
| 299 |
+
>>> [list(c) for c in children]
|
| 300 |
+
[[1], [2], [3], [], []]
|
| 301 |
+
>>> undistribute(children)
|
| 302 |
+
[1, 2, 3]
|
| 303 |
+
|
| 304 |
+
"""
|
| 305 |
+
|
| 306 |
+
return [
|
| 307 |
+
x
|
| 308 |
+
for x in itertools.chain.from_iterable(
|
| 309 |
+
itertools.zip_longest(*[list(x) for x in iterable])
|
| 310 |
+
)
|
| 311 |
+
if x is not None
|
| 312 |
+
]
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def retry_on_specific_exceptions(
|
| 316 |
+
on_exceptions: List[Type[Exception]],
|
| 317 |
+
max_retries: Optional[int] = None,
|
| 318 |
+
backoff_time: float = 3.0,
|
| 319 |
+
backoff_multiplier: float = 1.5,
|
| 320 |
+
on_exception_callback: Optional[Callable[[Exception, float], Any]] = None,
|
| 321 |
+
):
|
| 322 |
+
"""Retry on an LLM Provider's rate limit error with exponential backoff
|
| 323 |
+
For example, to use for OpenAI, do the following:
|
| 324 |
+
```
|
| 325 |
+
from openai import RateLimitError
|
| 326 |
+
|
| 327 |
+
# Recommend specifying max_retries to avoid infinite loops!
|
| 328 |
+
@retry_on_specific_exceptions([RateLimitError], max_retries=3)
|
| 329 |
+
def completion(...):
|
| 330 |
+
# Wrap OpenAI completion function here
|
| 331 |
+
...
|
| 332 |
+
```
|
| 333 |
+
"""
|
| 334 |
+
|
| 335 |
+
def decorator(func: Callable):
|
| 336 |
+
@wraps(func)
|
| 337 |
+
def wrapper(*args, **kwargs):
|
| 338 |
+
sleep_time = backoff_time
|
| 339 |
+
attempt = 0
|
| 340 |
+
while max_retries is None or attempt < max_retries:
|
| 341 |
+
try:
|
| 342 |
+
return func(*args, **kwargs)
|
| 343 |
+
except tuple(on_exceptions) as e:
|
| 344 |
+
if on_exception_callback is not None:
|
| 345 |
+
on_exception_callback(e, sleep_time)
|
| 346 |
+
time.sleep(sleep_time)
|
| 347 |
+
sleep_time *= backoff_multiplier
|
| 348 |
+
attempt += 1
|
| 349 |
+
|
| 350 |
+
return wrapper
|
| 351 |
+
|
| 352 |
+
return decorator
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
class Collator:
|
| 356 |
+
"""
|
| 357 |
+
A class for reordering and batching elements of an array.
|
| 358 |
+
|
| 359 |
+
This class allows for sorting an array based on a provided sorting function, grouping elements based on a grouping function, and generating batches from the sorted and grouped data.
|
| 360 |
+
|
| 361 |
+
Objects of this class have the group_by attribute which determines the method for grouping
|
| 362 |
+
the data while batching it. Three options include "gen_kwargs", "contexts", or None:
|
| 363 |
+
If group_by == "gen_kwargs" then requests will be grouped by gen_kwargs
|
| 364 |
+
If group_by == "contexts" then requests will be grouped by context + cont[:-1]
|
| 365 |
+
If None then requests will just be reordered by length descending.
|
| 366 |
+
"""
|
| 367 |
+
|
| 368 |
+
def __init__(
|
| 369 |
+
self,
|
| 370 |
+
arr: List,
|
| 371 |
+
sort_fn: Callable = lambda x: x,
|
| 372 |
+
group_fn: Callable = lambda x: x[1],
|
| 373 |
+
group_by: Union[Literal["gen_kwargs", "contexts"], None] = None,
|
| 374 |
+
) -> None:
|
| 375 |
+
self._group_by = group_by
|
| 376 |
+
# 0 indices are enumerated indices. Apply functions to original arr.
|
| 377 |
+
self._sort_fn = lambda x: sort_fn(x[1])
|
| 378 |
+
self._group_fn = lambda x: group_fn(x[1])
|
| 379 |
+
self._reorder_indices: List = []
|
| 380 |
+
self._size = len(arr)
|
| 381 |
+
self._arr_with_indices: Union[Dict, Tuple[Tuple[int, Any], ...]] = tuple(
|
| 382 |
+
enumerate(arr)
|
| 383 |
+
) # [indices, (arr)]
|
| 384 |
+
if self._group_by == "contexts":
|
| 385 |
+
self._group_by_context()
|
| 386 |
+
elif self._group_by == "gen_kwargs":
|
| 387 |
+
self._group_by_index()
|
| 388 |
+
|
| 389 |
+
def _group_by_index(self) -> None:
|
| 390 |
+
"""Group the elements of a list based on their indices."""
|
| 391 |
+
self._arr_with_indices = self.group(
|
| 392 |
+
self._arr_with_indices, fn=self._group_fn, group_by="gen_kwargs"
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
def _group_by_context(self) -> None:
|
| 396 |
+
"""Group the array with indices by context."""
|
| 397 |
+
self._arr_with_indices = self.group(
|
| 398 |
+
self._arr_with_indices, fn=self._group_fn, group_by="contexts"
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
def get_batched(self, n: int = 1, batch_fn: Optional[Callable] = None) -> Iterator:
|
| 402 |
+
"""
|
| 403 |
+
Generates and yields batches from the reordered array. The method of grouping and batching
|
| 404 |
+
depends on the parameter `group_by`.
|
| 405 |
+
If `group_by` is set to "gen_kwargs", it will batch the
|
| 406 |
+
re-ordered values with same gen_kwargs for each batch.
|
| 407 |
+
If `group_by` is "contexts", it caches the requests by context before batching.
|
| 408 |
+
If `group_by` is neither "gen_kwargs" nor "contexts", it yields the reordered array
|
| 409 |
+
|
| 410 |
+
Parameters:
|
| 411 |
+
- n (int): The size of each batch. Defaults to 1.
|
| 412 |
+
- batch_fn ([Callable[[int, Iterable], int]] | None): A function to determine the size of
|
| 413 |
+
each batch. Optional, defaults to None.
|
| 414 |
+
|
| 415 |
+
Returns:
|
| 416 |
+
Iterator: An iterator over batches of reordered elements grouped as per the `group_by`
|
| 417 |
+
attribute.
|
| 418 |
+
|
| 419 |
+
Yields:
|
| 420 |
+
List of batched elements according to the `group_by` attribute.
|
| 421 |
+
"""
|
| 422 |
+
if self._group_by == "gen_kwargs":
|
| 423 |
+
for (
|
| 424 |
+
key,
|
| 425 |
+
values,
|
| 426 |
+
) in self._arr_with_indices.items(): # type: ignore
|
| 427 |
+
values = self._reorder(values)
|
| 428 |
+
batch = self.get_chunks(values, n=n, fn=batch_fn)
|
| 429 |
+
yield from batch
|
| 430 |
+
elif self._group_by == "contexts":
|
| 431 |
+
# Get one sample from each key.
|
| 432 |
+
# Select longest continuation per group to ensure sufficient context logits
|
| 433 |
+
values = self._reorder(
|
| 434 |
+
[
|
| 435 |
+
max(value, key=lambda x: len(x[1][-1]))
|
| 436 |
+
for value in self._arr_with_indices.values()
|
| 437 |
+
]
|
| 438 |
+
)
|
| 439 |
+
batch = self.get_chunks(values, n=n, fn=batch_fn)
|
| 440 |
+
yield from batch
|
| 441 |
+
else:
|
| 442 |
+
values = self._reorder(self._arr_with_indices) # type: ignore
|
| 443 |
+
batch = self.get_chunks(values, n=n, fn=batch_fn)
|
| 444 |
+
yield from batch
|
| 445 |
+
|
| 446 |
+
def get_cache(
|
| 447 |
+
self,
|
| 448 |
+
req_str: Tuple[str, str] = None,
|
| 449 |
+
cxt_toks: List[int] = None,
|
| 450 |
+
cont_toks: List[int] = None,
|
| 451 |
+
logits: torch.Tensor = None,
|
| 452 |
+
) -> Iterator[Tuple[Tuple[str, str], List[int], torch.Tensor]]:
|
| 453 |
+
"""
|
| 454 |
+
Retrieves cached single-token continuations and their associated arguments, updating indices as necessary.
|
| 455 |
+
|
| 456 |
+
The behavior of this function varies depending on how the `group_by` attribute is set:
|
| 457 |
+
|
| 458 |
+
- When `group_by` is "contexts":
|
| 459 |
+
The function identifies single-token continuations by checking for keys that equate to
|
| 460 |
+
[context+continuation][-1] and logs the indices for re-ordering.
|
| 461 |
+
In this mode, this function can work in two scenarios:
|
| 462 |
+
|
| 463 |
+
1. Cache Hit - Single Match:
|
| 464 |
+
If a single matching context-continuation pair is found in the cache,
|
| 465 |
+
the function yields the original arguments.
|
| 466 |
+
|
| 467 |
+
2. Cache Hit - Multiple Matches:
|
| 468 |
+
If multiple matching context-continuation pairs are found in the cache,
|
| 469 |
+
the function expands the logits batch dimension to match the number of cache hits.
|
| 470 |
+
It updates the original requests and continuation tokens.
|
| 471 |
+
|
| 472 |
+
- When `group_by` is not set to "contexts":
|
| 473 |
+
This method yields the original arguments, logits and continuation tokens,
|
| 474 |
+
without checking for one-token continuations.
|
| 475 |
+
|
| 476 |
+
Parameters:
|
| 477 |
+
- req_str (tuple[str, str]): Original strings used for CachingLM.
|
| 478 |
+
- cxt_toks (list[int]): Full context tokens used for lookup.
|
| 479 |
+
- cont_toks (list[int]): Continuation tokens for which logits were generated.
|
| 480 |
+
- logits (torch.Tensor [1, seq_length, vocab_size]): Logits generated by the model given context and continuation keys.
|
| 481 |
+
|
| 482 |
+
Yields:
|
| 483 |
+
- Iterator:
|
| 484 |
+
- req_str (tuple[str, str]): strings used for CachingLM.
|
| 485 |
+
- cont_toks (list[int]) : continuation tokens.
|
| 486 |
+
- logits (torch.Tensor [1, seq_length, vocab_size]): The original logits (repeated cache hit times)
|
| 487 |
+
"""
|
| 488 |
+
if self._group_by == "contexts":
|
| 489 |
+
cache_hit: List[
|
| 490 |
+
Tuple[int, Tuple[Tuple[str, str], List[int], List[int]]]
|
| 491 |
+
] = self._arr_with_indices.pop(tuple(cxt_toks + cont_toks[:-1]))
|
| 492 |
+
if (cache_size := len(cache_hit)) == 1:
|
| 493 |
+
self._reorder_indices.extend(x[0] for x in cache_hit)
|
| 494 |
+
yield req_str, cont_toks, logits
|
| 495 |
+
else:
|
| 496 |
+
# If we have matching requests then expand the batch dimension (no-op) and
|
| 497 |
+
# yield each along with its corresponding args.
|
| 498 |
+
multilogits = logits.expand(cache_size, -1, -1).chunk(cache_size)
|
| 499 |
+
indices, req_str, cont_toks = zip(
|
| 500 |
+
*[(x[0], x[1][0], x[-1][-1]) for x in cache_hit]
|
| 501 |
+
)
|
| 502 |
+
self._reorder_indices.extend(indices)
|
| 503 |
+
for c_key, cont_tok, logit in zip(req_str, cont_toks, multilogits):
|
| 504 |
+
yield c_key, cont_tok, logit
|
| 505 |
+
else:
|
| 506 |
+
yield req_str, cont_toks, logits
|
| 507 |
+
|
| 508 |
+
def _reorder(self, arr: Union[List, Tuple[Tuple[int, Any], ...]]) -> Iterator:
|
| 509 |
+
"""
|
| 510 |
+
Reorders the elements in the array based on the sorting function.
|
| 511 |
+
|
| 512 |
+
Parameters:
|
| 513 |
+
- arr (list | tuple[tuple[int, Any], ...]]): The array or iterable to be reordered.
|
| 514 |
+
|
| 515 |
+
Yields:
|
| 516 |
+
Iterator
|
| 517 |
+
"""
|
| 518 |
+
arr = sorted(arr, key=self._sort_fn)
|
| 519 |
+
if not self._group_by == "contexts":
|
| 520 |
+
# If grouped by contexts then indices will be set in get_cache()
|
| 521 |
+
self._reorder_indices.extend([x[0] for x in arr])
|
| 522 |
+
yield from [x[1] for x in arr]
|
| 523 |
+
|
| 524 |
+
def get_original(self, newarr: List) -> List:
|
| 525 |
+
"""
|
| 526 |
+
Restores the original order of elements from the reordered list.
|
| 527 |
+
|
| 528 |
+
Parameters:
|
| 529 |
+
- newarr (list): The reordered array.
|
| 530 |
+
|
| 531 |
+
Returns:
|
| 532 |
+
list: The array with elements restored to their original order.
|
| 533 |
+
"""
|
| 534 |
+
res = [None] * self._size
|
| 535 |
+
cov = [False] * self._size
|
| 536 |
+
|
| 537 |
+
for ind, v in zip(self._reorder_indices, newarr):
|
| 538 |
+
res[ind] = v
|
| 539 |
+
cov[ind] = True
|
| 540 |
+
|
| 541 |
+
assert all(cov)
|
| 542 |
+
|
| 543 |
+
return res
|
| 544 |
+
|
| 545 |
+
def __len__(self):
|
| 546 |
+
return self._size
|
| 547 |
+
|
| 548 |
+
@staticmethod
|
| 549 |
+
def group(
|
| 550 |
+
arr: Iterable,
|
| 551 |
+
fn: Callable,
|
| 552 |
+
group_by: Literal["gen_kwargs", "contexts"] = "gen_kwargs",
|
| 553 |
+
) -> dict:
|
| 554 |
+
"""
|
| 555 |
+
Groups elements of an iterable based on a provided function.
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
The `group_by` parameter determines the method of grouping.
|
| 559 |
+
If `group_by` is "contexts", the elements are grouped by [context + cont][:-1].
|
| 560 |
+
If `group_by` is "gen_kwargs", the elements are grouped based on the gen_kwargs dict.
|
| 561 |
+
|
| 562 |
+
Parameters:
|
| 563 |
+
- arr (Iterable): The iterable to be grouped.
|
| 564 |
+
- fn (Callable): The function to determine the grouping.
|
| 565 |
+
- values (bool): If True, returns the values of the group. Defaults to False.
|
| 566 |
+
|
| 567 |
+
Returns:
|
| 568 |
+
Iterator: An iterable of grouped elements.
|
| 569 |
+
"""
|
| 570 |
+
res = collections.defaultdict(list)
|
| 571 |
+
for ob in arr:
|
| 572 |
+
# where ob == [context + cont]
|
| 573 |
+
if group_by == "contexts":
|
| 574 |
+
res[tuple(fn(ob))].append(ob)
|
| 575 |
+
else:
|
| 576 |
+
try:
|
| 577 |
+
hashable_dict = tuple(
|
| 578 |
+
(
|
| 579 |
+
key,
|
| 580 |
+
tuple(value)
|
| 581 |
+
if isinstance(value, collections.abc.Iterable)
|
| 582 |
+
else value,
|
| 583 |
+
)
|
| 584 |
+
for key, value in sorted(fn(ob).items())
|
| 585 |
+
)
|
| 586 |
+
res[hashable_dict].append(ob)
|
| 587 |
+
except (TypeError, AttributeError):
|
| 588 |
+
res[tuple(fn(ob))].append(ob)
|
| 589 |
+
return res
|
| 590 |
+
|
| 591 |
+
@staticmethod
|
| 592 |
+
def get_chunks(_iter, n: int = 0, fn=None):
|
| 593 |
+
"""
|
| 594 |
+
Divides an iterable into chunks of specified size or based on a given function.
|
| 595 |
+
Useful for batching
|
| 596 |
+
|
| 597 |
+
Parameters:
|
| 598 |
+
- iter: The input iterable to be divided into chunks.
|
| 599 |
+
- n: An integer representing the size of each chunk. Default is 0.
|
| 600 |
+
- fn: A function that takes the current index and the iterable as arguments and returns the size of the chunk. Default is None.
|
| 601 |
+
|
| 602 |
+
Returns:
|
| 603 |
+
An iterator that yields chunks of the input iterable.
|
| 604 |
+
|
| 605 |
+
Example usage:
|
| 606 |
+
```
|
| 607 |
+
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
| 608 |
+
for chunk in chunks(data, 3):
|
| 609 |
+
print(chunk)
|
| 610 |
+
```
|
| 611 |
+
Output:
|
| 612 |
+
```
|
| 613 |
+
[1, 2, 3]
|
| 614 |
+
[4, 5, 6]
|
| 615 |
+
[7, 8, 9]
|
| 616 |
+
[10]
|
| 617 |
+
```
|
| 618 |
+
"""
|
| 619 |
+
arr = []
|
| 620 |
+
_iter = tuple(_iter)
|
| 621 |
+
for i, x in enumerate(_iter):
|
| 622 |
+
arr.append(x)
|
| 623 |
+
if len(arr) == (fn(i, _iter) if fn else n):
|
| 624 |
+
yield arr
|
| 625 |
+
arr = []
|
| 626 |
+
|
| 627 |
+
if arr:
|
| 628 |
+
yield arr
|
| 629 |
+
|
| 630 |
+
|
| 631 |
+
def configure_pad_token(
|
| 632 |
+
tokenizer: "PreTrainedTokenizerBase",
|
| 633 |
+
model_config: Optional["PretrainedConfig"] = None,
|
| 634 |
+
) -> "PreTrainedTokenizerBase":
|
| 635 |
+
"""
|
| 636 |
+
This function checks if the (Hugging Face) tokenizer has a padding token and sets it if not present.
|
| 637 |
+
Some tokenizers require special handling.
|
| 638 |
+
|
| 639 |
+
Args:
|
| 640 |
+
tokenizer: The tokenizer for which the padding token is to be handled.
|
| 641 |
+
model_config: The configuration of the model. Default is None.
|
| 642 |
+
|
| 643 |
+
Returns:
|
| 644 |
+
The tokenizer after the padding token has been handled.
|
| 645 |
+
|
| 646 |
+
Raises:
|
| 647 |
+
AssertionError: If the tokenizer is of type RWKVWorldTokenizer or Rwkv5Tokenizer and the padding token id is not 0.
|
| 648 |
+
"""
|
| 649 |
+
if tokenizer.pad_token:
|
| 650 |
+
pass
|
| 651 |
+
elif tokenizer.unk_token:
|
| 652 |
+
tokenizer.pad_token_id = tokenizer.unk_token_id
|
| 653 |
+
elif tokenizer.eos_token:
|
| 654 |
+
tokenizer.pad_token_id = tokenizer.eos_token_id
|
| 655 |
+
else:
|
| 656 |
+
# handle special cases
|
| 657 |
+
if model_config and getattr(model_config, "model_type", None) == "qwen":
|
| 658 |
+
# Qwen's trust_remote_code tokenizer does not allow for adding special tokens
|
| 659 |
+
tokenizer.pad_token = "<|endoftext|>"
|
| 660 |
+
elif (
|
| 661 |
+
tokenizer.__class__.__name__ == "RWKVWorldTokenizer"
|
| 662 |
+
or tokenizer.__class__.__name__ == "Rwkv5Tokenizer"
|
| 663 |
+
):
|
| 664 |
+
# The RWKV world tokenizer, does not allow for adding special tokens / setting the pad token (which is set as 0)
|
| 665 |
+
# The additional tokenizer name check is needed, as there exists rwkv4 models with neox tokenizer
|
| 666 |
+
# ---
|
| 667 |
+
# Note that the world tokenizer class name, might change in the future for the final huggingface merge
|
| 668 |
+
# https://github.com/huggingface/transformers/pull/26963
|
| 669 |
+
assert tokenizer.pad_token_id == 0
|
| 670 |
+
else:
|
| 671 |
+
tokenizer.add_special_tokens({"pad_token": "<|pad|>"})
|
| 672 |
+
|
| 673 |
+
return tokenizer
|
| 674 |
+
|
| 675 |
+
|
| 676 |
+
def replace_placeholders(
|
| 677 |
+
string: str, default_placeholder: str, image_token: str, max_images: int
|
| 678 |
+
):
|
| 679 |
+
"""
|
| 680 |
+
A utility function used for local multimodal models. It locates all `placeholder` string
|
| 681 |
+
occurrences in the given input `string_` and replaces the first `max_count` instances with
|
| 682 |
+
`replacement`, and all subsequent occurrences with the empty string.
|
| 683 |
+
|
| 684 |
+
This is used to replace <image> placeholder tags by model-specific image tokens like <|image_pad|>
|
| 685 |
+
and to allow for only the first `max_count` images to be passed to a model if desired.
|
| 686 |
+
|
| 687 |
+
:param string: The original string containing placeholders.
|
| 688 |
+
:param default_placeholder: The placeholder text to be replaced.
|
| 689 |
+
:param image_token: The token to replace the placeholder with.
|
| 690 |
+
:param max_images: The maximum number of replacements to make.
|
| 691 |
+
:return: The string with placeholders replaced.
|
| 692 |
+
"""
|
| 693 |
+
count = 0
|
| 694 |
+
result = []
|
| 695 |
+
|
| 696 |
+
parts = string.split(default_placeholder)
|
| 697 |
+
for part in parts[:-1]: # Iterate through all but the last part
|
| 698 |
+
result.append(part)
|
| 699 |
+
if count < max_images:
|
| 700 |
+
result.append(image_token)
|
| 701 |
+
count += 1
|
| 702 |
+
elif default_placeholder != image_token:
|
| 703 |
+
result.append(default_placeholder)
|
| 704 |
+
|
| 705 |
+
# Add the last part of the string
|
| 706 |
+
result.append(parts[-1])
|
| 707 |
+
return "".join(result)
|
| 708 |
+
|
| 709 |
+
|
| 710 |
+
def flatten_image_list(images: List[List]):
|
| 711 |
+
"""
|
| 712 |
+
Takes in a list of lists of images, and returns a single list of all images in order.
|
| 713 |
+
Used for some multimodal models like Llava-1.5 which expects this flattened-list format for its image processor.
|
| 714 |
+
|
| 715 |
+
:param images: A list of lists of PIL images.
|
| 716 |
+
:return: a list of PIL images, via concatenating all the sub-lists in order.
|
| 717 |
+
"""
|
| 718 |
+
return [image for image_list in images for image in image_list]
|
| 719 |
+
|
| 720 |
+
|
| 721 |
+
def handle_stop_sequences(
|
| 722 |
+
until: Union[str, List[str], None], eos: Optional[str]
|
| 723 |
+
) -> List[str]:
|
| 724 |
+
"""Ensures that the `until` parameter is a list of stop sequences and includes the EOS token."""
|
| 725 |
+
if isinstance(until, str):
|
| 726 |
+
until = [until]
|
| 727 |
+
elif until is None:
|
| 728 |
+
until = []
|
| 729 |
+
elif not isinstance(until, list):
|
| 730 |
+
raise ValueError(
|
| 731 |
+
f"Expected `kwargs['until']` to be of type Union[str,list] but got {until}"
|
| 732 |
+
)
|
| 733 |
+
|
| 734 |
+
if eos is not None and eos not in until:
|
| 735 |
+
until.append(eos)
|
| 736 |
+
return until
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
def resize_image(
|
| 740 |
+
image: "Image.Image",
|
| 741 |
+
width: Optional[int] = None,
|
| 742 |
+
height: Optional[int] = None,
|
| 743 |
+
max_dimension: Optional[int] = None,
|
| 744 |
+
keep_aspect_ratio: bool = True,
|
| 745 |
+
resample_filter: Union[int, str] = "Image.BICUBIC",
|
| 746 |
+
min_width: int = 1,
|
| 747 |
+
min_height: int = 1,
|
| 748 |
+
) -> "Image.Image":
|
| 749 |
+
"""
|
| 750 |
+
Resizes a PIL Image object with flexible options.
|
| 751 |
+
|
| 752 |
+
Args:
|
| 753 |
+
image: The PIL Image object to resize.
|
| 754 |
+
width: Target width in pixels.
|
| 755 |
+
height: Target height in pixels.
|
| 756 |
+
max_dimension: Maximum size for the longer dimension of the image.
|
| 757 |
+
keep_aspect_ratio: If True (default) and both width and height are provided,
|
| 758 |
+
the image is resized to fit within these dimensions while
|
| 759 |
+
maintaining its aspect ratio. If False, the image is stretched
|
| 760 |
+
to the exact width and height.
|
| 761 |
+
resample_filter: The resampling filter to use for resizing.
|
| 762 |
+
Defaults to Image.BICUBIC.
|
| 763 |
+
min_width: Minimum width for the resized image. Defaults to 1.
|
| 764 |
+
min_height: Minimum height for the resized image. Defaults to 1.
|
| 765 |
+
|
| 766 |
+
Returns:
|
| 767 |
+
The resized PIL Image object. If no resize parameters are provided
|
| 768 |
+
or if the image already meets the criteria, the original image is returned.
|
| 769 |
+
|
| 770 |
+
Order of precedence for resizing:
|
| 771 |
+
1. If width AND height are provided:
|
| 772 |
+
- If keep_aspect_ratio is True: Fits image within bounds, preserving aspect ratio.
|
| 773 |
+
- If keep_aspect_ratio is False: Resizes to exact dimensions (may distort).
|
| 774 |
+
2. Else if only width is provided: Calculates height proportionally.
|
| 775 |
+
3. Else if only height is provided: Calculates width proportionally.
|
| 776 |
+
4. Else if max_dimension is provided: Resizes the longest side to max_dimension
|
| 777 |
+
and scales the other side proportionally.
|
| 778 |
+
5. If none of the above are provided, returns the original image.
|
| 779 |
+
"""
|
| 780 |
+
original_width, original_height = image.size
|
| 781 |
+
|
| 782 |
+
# If no arguments are provided, return the original image
|
| 783 |
+
if width is None and height is None and max_dimension is None:
|
| 784 |
+
return image
|
| 785 |
+
|
| 786 |
+
new_width = original_width
|
| 787 |
+
new_height = original_height
|
| 788 |
+
|
| 789 |
+
if width is not None and height is not None:
|
| 790 |
+
# No resize needed if image is already smaller than target dimensions
|
| 791 |
+
if original_width <= width and original_height <= height:
|
| 792 |
+
return image
|
| 793 |
+
|
| 794 |
+
if keep_aspect_ratio:
|
| 795 |
+
# Calculate the ratio to fit within the target dimensions
|
| 796 |
+
ratio = min(width / original_width, height / original_height)
|
| 797 |
+
new_width = int(original_width * ratio)
|
| 798 |
+
new_height = int(original_height * ratio)
|
| 799 |
+
else:
|
| 800 |
+
# Stretch to exact dimensions
|
| 801 |
+
new_width = width
|
| 802 |
+
new_height = height
|
| 803 |
+
elif width is not None:
|
| 804 |
+
# No resize needed if width is already smaller
|
| 805 |
+
if original_width <= width:
|
| 806 |
+
return image
|
| 807 |
+
# Calculate height proportionally
|
| 808 |
+
new_width = width
|
| 809 |
+
new_height = int((original_height / original_width) * new_width)
|
| 810 |
+
elif height is not None:
|
| 811 |
+
# No resize needed if height is already smaller
|
| 812 |
+
if original_height <= height:
|
| 813 |
+
return image
|
| 814 |
+
# Calculate width proportionally
|
| 815 |
+
new_height = height
|
| 816 |
+
new_width = int((original_width / original_height) * new_height)
|
| 817 |
+
elif max_dimension is not None:
|
| 818 |
+
# No resize needed if both dimensions are smaller than max_dimension
|
| 819 |
+
if max(original_height, original_width) <= max_dimension:
|
| 820 |
+
return image
|
| 821 |
+
|
| 822 |
+
if original_width > original_height:
|
| 823 |
+
# Width is the longer side
|
| 824 |
+
new_width = max_dimension
|
| 825 |
+
new_height = int((original_height / original_width) * new_width)
|
| 826 |
+
else:
|
| 827 |
+
# Height is the longer side or sides are equal
|
| 828 |
+
new_height = max_dimension
|
| 829 |
+
new_width = int((original_width / original_height) * new_height)
|
| 830 |
+
|
| 831 |
+
# Ensure dimensions are at least minimum values
|
| 832 |
+
new_width = max(min_width, new_width)
|
| 833 |
+
new_height = max(min_height, new_height)
|
| 834 |
+
|
| 835 |
+
# Perform the resize operation with the calculated dimensions
|
| 836 |
+
return image.resize((new_width, new_height), resample_filter)
|
| 837 |
+
|
| 838 |
+
|
| 839 |
+
def truncate_tokens(
|
| 840 |
+
tokens: List[int],
|
| 841 |
+
max_length: int,
|
| 842 |
+
tokenizer: "PreTrainedTokenizerBase",
|
| 843 |
+
strategy: str = "left",
|
| 844 |
+
):
|
| 845 |
+
if strategy == "left":
|
| 846 |
+
return tokens[-max_length:]
|
| 847 |
+
elif strategy == "right":
|
| 848 |
+
return tokens[:max_length]
|
| 849 |
+
elif strategy == "middle":
|
| 850 |
+
# Truncate the middle of the sequence
|
| 851 |
+
left_length = max_length // 2
|
| 852 |
+
right_length = max_length - left_length
|
| 853 |
+
return tokens[:left_length] + tokens[-right_length:]
|
| 854 |
+
return None
|
lm-evaluation-harness/lm_eval/models/vllm_vlms.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
import logging
|
| 3 |
+
from typing import Dict, List, Optional
|
| 4 |
+
|
| 5 |
+
import transformers
|
| 6 |
+
from more_itertools import distribute
|
| 7 |
+
from tqdm import tqdm
|
| 8 |
+
|
| 9 |
+
from lm_eval.api.instance import Instance
|
| 10 |
+
from lm_eval.api.registry import register_model
|
| 11 |
+
from lm_eval.models.utils import (
|
| 12 |
+
Collator,
|
| 13 |
+
handle_stop_sequences,
|
| 14 |
+
replace_placeholders,
|
| 15 |
+
resize_image,
|
| 16 |
+
undistribute,
|
| 17 |
+
)
|
| 18 |
+
from lm_eval.models.vllm_causallms import VLLM
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
eval_logger = logging.getLogger(__name__)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
import ray
|
| 26 |
+
from vllm import LLM, SamplingParams
|
| 27 |
+
from vllm.lora.request import LoRARequest # noqa: F401
|
| 28 |
+
from vllm.transformers_utils.tokenizer import get_tokenizer # noqa: F401
|
| 29 |
+
except ModuleNotFoundError:
|
| 30 |
+
pass
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
DEFAULT_IMAGE_PLACEHOLDER = "<image>"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@register_model("vllm-vlm")
|
| 37 |
+
class VLLM_VLM(VLLM):
|
| 38 |
+
MULTIMODAL = True
|
| 39 |
+
|
| 40 |
+
def __init__(
|
| 41 |
+
self,
|
| 42 |
+
pretrained: str,
|
| 43 |
+
trust_remote_code: Optional[bool] = False,
|
| 44 |
+
revision: Optional[str] = None,
|
| 45 |
+
interleave: bool = True,
|
| 46 |
+
# TODO<baber>: handle max_images and limit_mm_per_prompt better
|
| 47 |
+
max_images: int = 999,
|
| 48 |
+
image_width: Optional[int] = None,
|
| 49 |
+
image_height: Optional[int] = None,
|
| 50 |
+
image_max_side: Optional[int] = None,
|
| 51 |
+
**kwargs,
|
| 52 |
+
):
|
| 53 |
+
self.image_width = image_width
|
| 54 |
+
self.image_height = image_height
|
| 55 |
+
self.image_max_side = image_max_side
|
| 56 |
+
if self.image_max_side and (self.image_width or self.image_height):
|
| 57 |
+
raise ValueError(
|
| 58 |
+
"Ambiguous config for image resize: you can not specify both "
|
| 59 |
+
"image_max_side and (image_width or image_height)"
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
if max_images != 999:
|
| 63 |
+
kwargs["limit_mm_per_prompt"] = {"image": max_images}
|
| 64 |
+
eval_logger.info(f"Setting limit_mm_per_prompt[image] to {max_images}")
|
| 65 |
+
super().__init__(
|
| 66 |
+
pretrained=pretrained,
|
| 67 |
+
trust_remote_code=trust_remote_code,
|
| 68 |
+
revision=revision,
|
| 69 |
+
**kwargs,
|
| 70 |
+
)
|
| 71 |
+
self.interleave = interleave
|
| 72 |
+
self.max_images = max_images
|
| 73 |
+
self.processor = transformers.AutoProcessor.from_pretrained(
|
| 74 |
+
pretrained,
|
| 75 |
+
revision=revision,
|
| 76 |
+
trust_remote_code=trust_remote_code,
|
| 77 |
+
)
|
| 78 |
+
self.chat_applied: bool = False
|
| 79 |
+
|
| 80 |
+
def tok_batch_multimodal_encode(
|
| 81 |
+
self,
|
| 82 |
+
strings: List[str], # note that input signature of this fn is different
|
| 83 |
+
images, # TODO: typehint on this
|
| 84 |
+
left_truncate_len: int = None,
|
| 85 |
+
truncation: bool = False,
|
| 86 |
+
):
|
| 87 |
+
images = [img[: self.max_images] for img in images]
|
| 88 |
+
# TODO<baber>: is the default placeholder always <image>?
|
| 89 |
+
if self.chat_applied is False:
|
| 90 |
+
strings = [
|
| 91 |
+
replace_placeholders(
|
| 92 |
+
string,
|
| 93 |
+
DEFAULT_IMAGE_PLACEHOLDER,
|
| 94 |
+
DEFAULT_IMAGE_PLACEHOLDER,
|
| 95 |
+
self.max_images,
|
| 96 |
+
)
|
| 97 |
+
for string in strings
|
| 98 |
+
]
|
| 99 |
+
|
| 100 |
+
outputs = []
|
| 101 |
+
for x, i in zip(strings, images):
|
| 102 |
+
inputs = {
|
| 103 |
+
"prompt": x,
|
| 104 |
+
"multi_modal_data": {"image": i},
|
| 105 |
+
}
|
| 106 |
+
outputs.append(inputs)
|
| 107 |
+
return outputs
|
| 108 |
+
|
| 109 |
+
def _multimodal_model_generate(
|
| 110 |
+
self,
|
| 111 |
+
requests: List[List[dict]] = None,
|
| 112 |
+
generate: bool = False,
|
| 113 |
+
max_tokens: int = None,
|
| 114 |
+
stop: Optional[List[str]] = None,
|
| 115 |
+
**kwargs,
|
| 116 |
+
):
|
| 117 |
+
if generate:
|
| 118 |
+
kwargs = self.modify_gen_kwargs(kwargs)
|
| 119 |
+
sampling_params = SamplingParams(max_tokens=max_tokens, stop=stop, **kwargs)
|
| 120 |
+
else:
|
| 121 |
+
sampling_params = SamplingParams(
|
| 122 |
+
temperature=0, prompt_logprobs=1, max_tokens=1, detokenize=False
|
| 123 |
+
)
|
| 124 |
+
if self.data_parallel_size > 1:
|
| 125 |
+
# vLLM hangs if resources are set in ray.remote
|
| 126 |
+
# also seems to only work with decorator and not with ray.remote() fn
|
| 127 |
+
# see https://github.com/vllm-project/vllm/issues/973
|
| 128 |
+
@ray.remote
|
| 129 |
+
def run_inference_one_model(
|
| 130 |
+
model_args: dict, sampling_params, requests: List[List[dict]]
|
| 131 |
+
):
|
| 132 |
+
llm = LLM(**model_args)
|
| 133 |
+
return llm.generate(requests, sampling_params=sampling_params)
|
| 134 |
+
|
| 135 |
+
# dispatch requests to all self.data_parallel_size workers, in interleaved fashion
|
| 136 |
+
# interleaved important to balance context lengths across workers
|
| 137 |
+
requests = [list(x) for x in distribute(self.data_parallel_size, requests)]
|
| 138 |
+
inputs = ((self.model_args, sampling_params, req) for req in requests)
|
| 139 |
+
object_refs = [run_inference_one_model.remote(*x) for x in inputs]
|
| 140 |
+
results = ray.get(object_refs)
|
| 141 |
+
# Invoke ray.shutdown() to prevent hang-ups if subsequent calls required.
|
| 142 |
+
ray.shutdown()
|
| 143 |
+
# flatten results
|
| 144 |
+
return undistribute(results)
|
| 145 |
+
|
| 146 |
+
if self.lora_request is not None:
|
| 147 |
+
outputs = self.model.generate(
|
| 148 |
+
requests,
|
| 149 |
+
sampling_params=sampling_params,
|
| 150 |
+
use_tqdm=True if self.batch_size == "auto" else False,
|
| 151 |
+
lora_request=self.lora_request,
|
| 152 |
+
)
|
| 153 |
+
else:
|
| 154 |
+
outputs = self.model.generate(
|
| 155 |
+
requests,
|
| 156 |
+
sampling_params=sampling_params,
|
| 157 |
+
use_tqdm=True if self.batch_size == "auto" else False,
|
| 158 |
+
)
|
| 159 |
+
return outputs
|
| 160 |
+
|
| 161 |
+
def apply_chat_template(
|
| 162 |
+
self, chat_history: List[Dict[str, str]], add_generation_prompt=True
|
| 163 |
+
) -> str:
|
| 164 |
+
self.chat_applied = True
|
| 165 |
+
if not self.interleave:
|
| 166 |
+
for content in chat_history:
|
| 167 |
+
c = []
|
| 168 |
+
text = content["content"]
|
| 169 |
+
|
| 170 |
+
# Count and remove image placeholders
|
| 171 |
+
image_count = min(
|
| 172 |
+
self.max_images, text.count(DEFAULT_IMAGE_PLACEHOLDER)
|
| 173 |
+
)
|
| 174 |
+
text = text.replace(DEFAULT_IMAGE_PLACEHOLDER, "")
|
| 175 |
+
|
| 176 |
+
# Add image entries
|
| 177 |
+
for _ in range(image_count):
|
| 178 |
+
c.append({"type": "image", "image": None})
|
| 179 |
+
|
| 180 |
+
# Add single text entry at the end
|
| 181 |
+
c.append({"type": "text", "text": text})
|
| 182 |
+
|
| 183 |
+
content["content"] = c
|
| 184 |
+
else:
|
| 185 |
+
for content in chat_history:
|
| 186 |
+
c = []
|
| 187 |
+
text = content["content"]
|
| 188 |
+
expected_image_count = min(
|
| 189 |
+
self.max_images, text.count(DEFAULT_IMAGE_PLACEHOLDER)
|
| 190 |
+
)
|
| 191 |
+
actual_image_count = 0
|
| 192 |
+
|
| 193 |
+
text_parts = text.split(DEFAULT_IMAGE_PLACEHOLDER)
|
| 194 |
+
|
| 195 |
+
for i, part in enumerate(text_parts):
|
| 196 |
+
# TODO: concatenate text parts (esp. if skipping images)?
|
| 197 |
+
if part: # Add non-empty text parts
|
| 198 |
+
c.append({"type": "text", "text": part})
|
| 199 |
+
if (
|
| 200 |
+
(i < len(text_parts) - 1) and i < self.max_images
|
| 201 |
+
): # Add image placeholder after each split except the last
|
| 202 |
+
c.append({"type": "image"})
|
| 203 |
+
actual_image_count += 1
|
| 204 |
+
|
| 205 |
+
content["content"] = c
|
| 206 |
+
|
| 207 |
+
if actual_image_count != expected_image_count:
|
| 208 |
+
raise ValueError(
|
| 209 |
+
f"Mismatch in image placeholder count. Expected: {expected_image_count}, Actual: {actual_image_count}"
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
return self.processor.apply_chat_template(
|
| 213 |
+
chat_history,
|
| 214 |
+
add_generation_prompt=add_generation_prompt,
|
| 215 |
+
continue_final_message=not add_generation_prompt,
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
def generate_until(
|
| 219 |
+
self, requests: List[Instance], disable_tqdm: bool = False
|
| 220 |
+
) -> List[str]:
|
| 221 |
+
if requests and len(requests[0].args) < 3:
|
| 222 |
+
# Fall back to non-multimodal generation.
|
| 223 |
+
return super().generate_until(requests=requests, disable_tqdm=disable_tqdm)
|
| 224 |
+
|
| 225 |
+
res = []
|
| 226 |
+
|
| 227 |
+
def _collate(x):
|
| 228 |
+
# the negative sign on len(toks) sorts descending - this has a few advantages:
|
| 229 |
+
# - time estimates will always be over not underestimates, which is more useful for planning
|
| 230 |
+
# - to know the size of a batch when going through the list, you know the first one is always the batch
|
| 231 |
+
# padded context length. this is useful to simplify the batching logic and more importantly to make
|
| 232 |
+
# automatic adaptive batches much much easier to implement
|
| 233 |
+
# - any OOMs will happen right away rather than near the end
|
| 234 |
+
toks = self.tok_encode(x[0])
|
| 235 |
+
return -len(toks), x[0]
|
| 236 |
+
|
| 237 |
+
pbar = tqdm(
|
| 238 |
+
total=len(requests),
|
| 239 |
+
disable=(disable_tqdm or (self.rank != 0)),
|
| 240 |
+
desc="Running generate_until requests with text+image input",
|
| 241 |
+
)
|
| 242 |
+
# TODO: port auto-batch sizing into this.
|
| 243 |
+
|
| 244 |
+
# we group requests by their generation_kwargs,
|
| 245 |
+
# so that we don't try to execute e.g. greedy sampling and temp=0.8 sampling
|
| 246 |
+
# in the same batch.
|
| 247 |
+
re_ords = Collator(
|
| 248 |
+
[reg.args for reg in requests],
|
| 249 |
+
_collate,
|
| 250 |
+
group_by="gen_kwargs",
|
| 251 |
+
group_fn=lambda x: x[1],
|
| 252 |
+
)
|
| 253 |
+
chunks = re_ords.get_batched(n=self.batch_size, batch_fn=None)
|
| 254 |
+
eos = self.tokenizer.decode(self.eot_token_id)
|
| 255 |
+
for chunk in chunks:
|
| 256 |
+
contexts, all_gen_kwargs, aux_arguments = zip(*chunk)
|
| 257 |
+
|
| 258 |
+
visuals = [
|
| 259 |
+
[
|
| 260 |
+
resize_image(
|
| 261 |
+
img, self.image_width, self.image_height, self.image_max_side
|
| 262 |
+
)
|
| 263 |
+
for img in arg["visual"]
|
| 264 |
+
]
|
| 265 |
+
for arg in aux_arguments
|
| 266 |
+
]
|
| 267 |
+
|
| 268 |
+
if not isinstance(contexts, list):
|
| 269 |
+
contexts = list(
|
| 270 |
+
contexts
|
| 271 |
+
) # for Qwen2-VL, processor is unhappy accepting a tuple of strings instead of a list.
|
| 272 |
+
# TODO: could we upstream this workaround to HF?
|
| 273 |
+
|
| 274 |
+
# we assume all gen kwargs in the batch are the same
|
| 275 |
+
# this is safe to assume because the `grouper` object ensures it.
|
| 276 |
+
gen_kwargs = all_gen_kwargs[0]
|
| 277 |
+
# unpack our keyword arguments.
|
| 278 |
+
if isinstance(gen_kwargs, dict):
|
| 279 |
+
kwargs = copy.deepcopy(gen_kwargs) # edge case for repeats > 1
|
| 280 |
+
# add EOS token to stop sequences
|
| 281 |
+
until = handle_stop_sequences(kwargs.pop("until", None), eos=eos)
|
| 282 |
+
else:
|
| 283 |
+
raise ValueError(
|
| 284 |
+
f"Expected `kwargs` to be of type `dict` but got {type(gen_kwargs)}"
|
| 285 |
+
)
|
| 286 |
+
if "max_gen_toks" in kwargs.keys():
|
| 287 |
+
max_gen_toks = kwargs.pop("max_gen_toks")
|
| 288 |
+
else:
|
| 289 |
+
max_gen_toks = self.max_gen_toks
|
| 290 |
+
|
| 291 |
+
max_ctx_len = self.max_length - max_gen_toks
|
| 292 |
+
|
| 293 |
+
inputs = self.tok_batch_multimodal_encode(
|
| 294 |
+
contexts,
|
| 295 |
+
visuals,
|
| 296 |
+
left_truncate_len=max_ctx_len,
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
cont = self._multimodal_model_generate(
|
| 300 |
+
inputs, stop=until, generate=True, max_tokens=max_gen_toks, **kwargs
|
| 301 |
+
)
|
| 302 |
+
|
| 303 |
+
for output, context in zip(cont, contexts):
|
| 304 |
+
generated_text = output.outputs[0].text
|
| 305 |
+
res.append(generated_text)
|
| 306 |
+
self.cache_hook.add_partial(
|
| 307 |
+
"generate_until", (context, gen_kwargs), generated_text
|
| 308 |
+
)
|
| 309 |
+
pbar.update(1)
|
| 310 |
+
# reorder this group of results back to original unsorted form
|
| 311 |
+
res = re_ords.get_original(res)
|
| 312 |
+
|
| 313 |
+
pbar.close()
|
| 314 |
+
return res
|
| 315 |
+
|
| 316 |
+
def loglikelihood_rolling(self, requests: List[Instance]) -> List[float]:
|
| 317 |
+
if requests and len(requests[0].args) < 3:
|
| 318 |
+
# Fall back to non-multimodal generation.
|
| 319 |
+
return super().loglikelihood_rolling(requests=requests)
|
| 320 |
+
raise NotImplementedError(
|
| 321 |
+
"model type `vllm-vlm` does not support loglikelihood_rolling. Use 'vlm' model type for text-only loglikelihood_rolling tasks ",
|
| 322 |
+
"this is because we do not support measuring the loglikelihood a model assigns to an image.",
|
| 323 |
+
)
|
lm-evaluation-harness/lm_eval/tasks/aclue/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ACLUE
|
| 2 |
+
|
| 3 |
+
### Paper
|
| 4 |
+
|
| 5 |
+
Can Large Language Model Comprehend Ancient Chinese? A Preliminary Test on ACLUE
|
| 6 |
+
https://arxiv.org/abs/2310.09550
|
| 7 |
+
|
| 8 |
+
The Ancient Chinese Language Understanding Evaluation (ACLUE) is an evaluation benchmark focused on ancient Chinese language comprehension. It aims to assess the performance of large-scale language models on understanding ancient Chinese. The benchmark comprises 15 tasks spanning various domains, including lexical, syntactic, semantic, inference, and knowledge. ACLUE's tasks are derived from a combination of manually curated questions from publicly available resources, and automatically
|
| 9 |
+
generated questions from classical Chinese language corpora. The range of questions span from the Xia dynasty (2070 BCE) to the Ming dynasty (1368 CE). ACLUE adopts a multiple-choice question format for all tasks.
|
| 10 |
+
|
| 11 |
+
Homepage: https://github.com/isen-zhang/ACLUE
|
| 12 |
+
|
| 13 |
+
### Citation
|
| 14 |
+
|
| 15 |
+
```bibtex
|
| 16 |
+
@inproceedings{zhang-li-2023-large,
|
| 17 |
+
title = "Can Large Language Model Comprehend {A}ncient {C}hinese? A Preliminary Test on {ACLUE}",
|
| 18 |
+
author = "Zhang, Yixuan and Li, Haonan",
|
| 19 |
+
booktitle = "Proceedings of the Ancient Language Processing Workshop",
|
| 20 |
+
month = sep,
|
| 21 |
+
year = "2023",
|
| 22 |
+
address = "Varna, Bulgaria",
|
| 23 |
+
publisher = "INCOMA Ltd., Shoumen, Bulgaria",
|
| 24 |
+
url = "https://aclanthology.org/2023.alp-1.9",
|
| 25 |
+
pages = "80--87"
|
| 26 |
+
}
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
### Groups, Tags, and Tasks
|
| 30 |
+
|
| 31 |
+
#### Groups
|
| 32 |
+
|
| 33 |
+
- `aclue`: All 15 subjects of the ACLUE dataset, evaluated following the methodology in CMMLU's original implementation.
|
| 34 |
+
|
| 35 |
+
#### Tasks
|
| 36 |
+
|
| 37 |
+
The following tasks evaluate subjects in the ACLUE dataset using loglikelihood-based multiple-choice scoring:
|
| 38 |
+
- `aclue_{subject_english}`
|
| 39 |
+
|
| 40 |
+
### Checklist
|
| 41 |
+
|
| 42 |
+
* [x] Is the task an existing benchmark in the literature?
|
| 43 |
+
* [x] Have you referenced the original paper that introduced the task?
|
| 44 |
+
* [x] If yes, does the original paper provide a reference implementation?
|
| 45 |
+
* [x] Yes, original implementation contributed by author of the benchmark
|
| 46 |
+
|
| 47 |
+
If other tasks on this dataset are already supported:
|
| 48 |
+
* [x] Is the "Main" variant of this task clearly denoted?
|
| 49 |
+
* [x] Have you provided a short sentence in a README on what each new variant adds / evaluates?
|
| 50 |
+
* [x] Have you noted which, if any, published evaluation setups are matched by this variant?
|
lm-evaluation-harness/lm_eval/tasks/aclue/aclue_named_entity_recognition.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"dataset_name": "named_entity_recognition"
|
| 2 |
+
"description": "以下是关于古汉语命名体识别的单项选择题,请直接给出正确答案的选项。\n\n"
|
| 3 |
+
"include": "_default_template_yaml"
|
| 4 |
+
"task": "aclue_named_entity_recognition"
|
lm-evaluation-harness/lm_eval/tasks/aclue/aclue_poetry_context_prediction.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"dataset_name": "poetry_context_prediction"
|
| 2 |
+
"description": "以下是关于古诗词上下句预测的单项选择题,请直接给出正确答案的选项。\n\n"
|
| 3 |
+
"include": "_default_template_yaml"
|
| 4 |
+
"task": "aclue_poetry_context_prediction"
|
lm-evaluation-harness/lm_eval/tasks/aclue/aclue_reading_comprehension.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"dataset_name": "reading_comprehension"
|
| 2 |
+
"description": "以下是关于古文阅读理解的单项选择题,请直接给出正确答案的选项。\n\n"
|
| 3 |
+
"include": "_default_template_yaml"
|
| 4 |
+
"task": "aclue_reading_comprehension"
|
lm-evaluation-harness/lm_eval/tasks/aclue/aclue_sentence_segmentation.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"dataset_name": "sentence_segmentation"
|
| 2 |
+
"description": "以下是关于古文断句的单项选择题,请直接给出正确答案的选项。\n\n"
|
| 3 |
+
"include": "_default_template_yaml"
|
| 4 |
+
"task": "aclue_sentence_segmentation"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ACPBench
|
| 2 |
+
|
| 3 |
+
**Homepage:** https://ibm.github.io/ACPBench/
|
| 4 |
+
|
| 5 |
+
### Papers
|
| 6 |
+
|
| 7 |
+
**Title:** ACPBench: Reasoning About Action, Change, and Planning
|
| 8 |
+
**Pdf:** https://arxiv.org/pdf/2410.05669
|
| 9 |
+
**Task:** `acp_bench`
|
| 10 |
+
**Abstract:**
|
| 11 |
+
|
| 12 |
+
There is an increasing body of work using Large Language Models (LLMs) as agents for orchestrating workflows and making decisions in domains that require planning and multi-step reasoning. As a result, it is imperative to evaluate LMs on core skills required for planning. ACPBench is a benchmark for evaluating the reasoning tasks in the field of planning. The benchmark consists of 7 reasoning tasks over 13 planning domains. The collection is constructed from planning domains described in a formal language. This allows the synthesized problems to have provably correct solutions across many tasks and domains. Further, it allows the luxury to scale without additional human effort, i.e., many additional problems can be created automatically.
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
**Title:** ACPBench Hard: Unrestrained Reasoning about Action, Change, and Planning
|
| 17 |
+
**Pdf:** https://arxiv.org/abs/2503.24378
|
| 18 |
+
**Task:** `acp_bench_hard`
|
| 19 |
+
**Abstract:**
|
| 20 |
+
|
| 21 |
+
We introduce ACPBench Hard, a dataset of generative, open-ended questions which LLM models needs to answer in order to plan. Models that perform well on these tasks could in principle be integrated into a planner or be used directly as a policy. We discuss the complexity of these tasks as well as the complexity of validating the correctness of their answers and present validation algorithms for each task. Equipped with these validators, we test the performance of a variety of models on our tasks and find that for most of these tasks, the performance of even the largest models is still subpar. Our experiments show that no model outperforms any other in these tasks, and with a few exceptions, all tested language models score below 65\%, indicating that even the current frontier language models as well as so-called reasoning models have a long way to go before they can reliably reason about planning.
|
| 22 |
+
|
| 23 |
+
The dataset is available on [HuggingFace](https://huggingface.co/datasets/ibm-research/acp_bench).
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
### Citation
|
| 27 |
+
|
| 28 |
+
```
|
| 29 |
+
@inproceedings{kokel2025acp
|
| 30 |
+
author = {Harsha Kokel and
|
| 31 |
+
Michael Katz and
|
| 32 |
+
Kavitha Srinivas and
|
| 33 |
+
Shirin Sohrabi},
|
| 34 |
+
title = {ACPBench: Reasoning about Action, Change, and Planning},
|
| 35 |
+
booktitle = {{AAAI}},
|
| 36 |
+
publisher = {{AAAI} Press},
|
| 37 |
+
year = {2025}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
@misc{KokelKSS25ACPHard,
|
| 41 |
+
title = {ACPBench Hard: Unrestrained Reasoning about Action, Change, and Planning},
|
| 42 |
+
author = {Harsha Kokel and
|
| 43 |
+
Michael Katz and
|
| 44 |
+
Kavitha Srinivas and
|
| 45 |
+
Shirin Sohrabi},
|
| 46 |
+
year = {2025},
|
| 47 |
+
eprint = {2503.24378},
|
| 48 |
+
archivePrefix = {arXiv},
|
| 49 |
+
primaryClass = {cs.AI},
|
| 50 |
+
url = {https://arxiv.org/abs/2503.24378},
|
| 51 |
+
}
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
### Groups, Tags, and Tasks
|
| 55 |
+
|
| 56 |
+
#### Groups
|
| 57 |
+
|
| 58 |
+
* None
|
| 59 |
+
|
| 60 |
+
#### Tags
|
| 61 |
+
|
| 62 |
+
* `acp_bench` : Evaluates `acp_bool_cot_2shot` and `acp_mcq_cot_2shot` (Main variant for ACPBench paper)
|
| 63 |
+
* `acp_bool_cot_2shot` : Evaluates `acp_areach_bool`, `acp_app_bool`, `acp_just_bool`, `acp_land_bool`, `acp_prog_bool`, `acp_reach_bool`, `acp_val_bool` with chain-of-thought and 2 shots
|
| 64 |
+
* `acp_mcq_cot_2shot` : Evaluates `acp_areach_mcq`, `acp_app_mcq`, `acp_just_mcq`, `acp_land_mcq`, `acp_prog_mcq`, `acp_reach_mcq`, `acp_val_mcq` with chain-of-thought and 2 shots
|
| 65 |
+
* `acp_bench_hard` : Evaluates `acp_gen_2shot` (Main variant for ACPBench Hard paper)
|
| 66 |
+
* `acp_gen_2shot` : Evaluates `acp_areach_gen`, `acp_app_gen`, `acp_just_gen`, `acp_land_gen`, `acp_nexta_gen`, `acp_prog_gen`, `acp_reach_gen`, `acp_val_gen` with 2 shots
|
| 67 |
+
* `acp_bench_hard_with_pddl` : Evaluates `acp_gen_2shot_with_pddl`
|
| 68 |
+
* `acp_gen_2shot_with_pddl` : Evaluates `acp_areach_gen_with_pddl`, `acp_app_gen_with_pddl`, `acp_just_gen_with_pddl`, `acp_land_gen_with_pddl`, `acp_nexta_gen_with_pddl`, `acp_prog_gen_with_pddl`, `acp_reach_gen_with_pddl`, `acp_val_gen_with_pddl` with 2 shots
|
| 69 |
+
|
| 70 |
+
#### Tasks
|
| 71 |
+
|
| 72 |
+
7 Boolean tasks
|
| 73 |
+
* `acp_areach_bool`
|
| 74 |
+
* `acp_app_bool`
|
| 75 |
+
* `acp_just_bool`
|
| 76 |
+
* `acp_land_bool`
|
| 77 |
+
* `acp_prog_bool`
|
| 78 |
+
* `acp_reach_bool`
|
| 79 |
+
* `acp_val_bool`
|
| 80 |
+
|
| 81 |
+
7 MCQ tasks
|
| 82 |
+
* `acp_areach_mcq`
|
| 83 |
+
* `acp_app_mcq`
|
| 84 |
+
* `acp_just_mcq`
|
| 85 |
+
* `acp_land_mcq`
|
| 86 |
+
* `acp_prog_mcq`
|
| 87 |
+
* `acp_reach_mcq`
|
| 88 |
+
* `acp_val_mcq`
|
| 89 |
+
|
| 90 |
+
8 Generative tasks (with just natural language description in context)
|
| 91 |
+
* `acp_areach_gen`
|
| 92 |
+
* `acp_app_gen`
|
| 93 |
+
* `acp_just_gen`
|
| 94 |
+
* `acp_land_gen`
|
| 95 |
+
* `acp_nexta_gen`
|
| 96 |
+
* `acp_prog_gen`
|
| 97 |
+
* `acp_reach_gen`
|
| 98 |
+
* `acp_val_gen`
|
| 99 |
+
|
| 100 |
+
and the same 8 generative tasks with natural language as well as the PDDL description of the domain and problem in context.
|
| 101 |
+
* `acp_areach_gen_with_pddl`
|
| 102 |
+
* `acp_app_gen_with_pddl`
|
| 103 |
+
* `acp_just_gen_with_pddl`
|
| 104 |
+
* `acp_land_gen_with_pddl`
|
| 105 |
+
* `acp_nexta_gen_with_pddl`
|
| 106 |
+
* `acp_prog_gen_with_pddl`
|
| 107 |
+
* `acp_reach_gen_with_pddl`
|
| 108 |
+
* `acp_val_gen_with_pddl`
|
| 109 |
+
|
| 110 |
+
> ! The evaluation scripts are taken from original github https://github.com/IBM/ACPBench
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
### Checklist
|
| 114 |
+
|
| 115 |
+
For adding novel benchmarks/datasets to the library:
|
| 116 |
+
* [x] Is the task an existing benchmark in the literature?
|
| 117 |
+
* [x] Have you referenced the original paper that introduced the task?
|
| 118 |
+
* [x] If yes, does the original paper provide a reference implementation? If so, have you checked against the reference implementation and documented how to run such a test?
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
If other tasks on this dataset are already supported:
|
| 122 |
+
* [x] Is the "Main" variant of this task clearly denoted?
|
| 123 |
+
* [x] Have you provided a short sentence in a README on what each new variant adds / evaluates?
|
| 124 |
+
* [x] Have you noted which, if any, published evaluation setups are matched by this variant?
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
### Change Log
|
| 128 |
+
|
| 129 |
+
* 03/17/2025 Initial Commit
|
| 130 |
+
* 05/13/2025 Adding ACPBench Hard tasks (with and without PDDL)
|
lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/_boolq_cot_2shot_yaml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- acp_bool_cot_2shot
|
| 3 |
+
- acp_bench
|
| 4 |
+
output_type: generate_until
|
| 5 |
+
dataset_path: ibm-research/acp_bench
|
| 6 |
+
test_split: test
|
| 7 |
+
num_fewshot: 2
|
| 8 |
+
doc_to_target: "{{answer}}"
|
| 9 |
+
doc_to_text: "**Question**: {{context}} {{question}} **Thoughts**:"
|
| 10 |
+
filter_list:
|
| 11 |
+
- name: "extract-yes-no"
|
| 12 |
+
filter:
|
| 13 |
+
- function: "regex"
|
| 14 |
+
regex_pattern: "((?<=The answer is )(.*)(?=.)|(?<=the answer is )(.*)(?=.)|(?<=The answer: )(.*)(?=.)|(?<=The final answer: )(.*)(?=.)|(?<=..Final Answer..: )(.*)(?=.)|(?<=..answer..: )(.*)(?=.)|(?<=..Answer..: )(.*)(?=.)|\\b(Yes|No|yes|no)\\b)"
|
| 15 |
+
- function: "take_first"
|
| 16 |
+
generation_kwargs:
|
| 17 |
+
until:
|
| 18 |
+
- "**Question**:"
|
| 19 |
+
- "**Question:**"
|
| 20 |
+
- "Q:"
|
| 21 |
+
- "\n\n\n\n"
|
| 22 |
+
do_sample: false
|
| 23 |
+
temperature: 0.0
|
| 24 |
+
max_gen_toks: 1024
|
| 25 |
+
metric_list:
|
| 26 |
+
- metric: exact_match
|
| 27 |
+
aggregation: mean
|
| 28 |
+
higher_is_better: true
|
| 29 |
+
ignore_case: true
|
| 30 |
+
ignore_punctuation: true
|
| 31 |
+
metadata:
|
| 32 |
+
version: 1.0
|
lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/app.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_app_bool
|
| 2 |
+
dataset_name: acp_app_bool
|
| 3 |
+
include: _boolq_cot_2shot_yaml
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: 'This is a ferry domain, where the task is to transport cars from their start to their goal locations, using a ferry. Each location is accessible by ferry from each other location. The cars can be debarked or boarded, and the ferry can carry only one car at a time. There are 2 locations and 2 cars, numbered consecutively. Currently, the ferry is at l0, with the car c1 on board. The cars are at locations as follows: c0 is at l0.'
|
| 8 |
+
question: 'Is the following action applicable in this state: travel by sea from location l1 to location l0?'
|
| 9 |
+
answer: "Step 1: In order to apply the action travel by sea from location l1 to location l0, the following fact(s) must hold in this state: The ferry is at l1 location Step 2: These facts do not hold in the mentioned state: The ferry is at l1 location. So, the action is not applicable. **Final Answer**: No."
|
| 10 |
+
- context: 'There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 3 trucks and 1 airplane, as well as 7 packages. There are 9 locations across 3 cities. The locations are in cities as follows: l1-2, l1-0, and l1-1 are in c1; l2-0, l2-1, and l2-2 are in c2; l0-0, l0-2, and l0-1 are in c0. Currently, p0 and p1 are at l1-2, a0 and p2 are at l0-0, t1 is at l1-0, t0 and p4 are at l0-2, t2 is at l2-0, p6, p3, and p5 are in t0.'
|
| 11 |
+
question: 'Is the following action applicable in this state: load the object p4 from location l0-2 into the truck t0?'
|
| 12 |
+
answer: "Let's think step by step. Step 1: In order to apply the action \"load the object p4 from location l0-2 into the truck t0\", the following fact(s) must hold in this state: p4 is at l0-2 and t0 is at l0-2 Step 2: These facts hold in the mentioned state, so the action is applicable. **Final Answer**: Yes."
|
lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/just.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_just_bool
|
| 2 |
+
dataset_name: acp_just_bool
|
| 3 |
+
include: _boolq_cot_2shot_yaml
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: 'This is a ferry domain, where the task is to transport cars from their start to their goal locations, using a ferry. Each location is accessible by ferry from each other location. The cars can be debarked or boarded, and the ferry can carry only one car at a time. There are 2 locations and 2 cars, numbered consecutively. Currently, the ferry is at l0 location and it is empty. The cars are at locations as follows: c0 and c1 are at l0. The goal is to reach a state where the following facts hold: Car c1 is at location l1 and Car c0 is at location l1'
|
| 8 |
+
question: 'Given the plan: \"board the car c1 at the location l0, sail from location l0 to location l1, unload the car c1 from the ferry to location l1, travel by sea from location l1 to location l0, board the car c0 at location l0, travel by sea from location l0 to location l1, unload the car c0 from the ferry to location l1, embark the car c1 at location l1 on to the ferry, debark car c1 to location l1 from the ferry\"; can the following pair of consecutive actions be removed from this plan and still have a valid plan: embark the car c1 at location l1 on to the ferry and debark the car c1 to location l1 from the ferry?'
|
| 9 |
+
answer: "Let's think step by step. Step 1: A valid plan is a sequence of actions that is executable from the current state and achieves the goal. Step 2: Consecutive actions \"embark the car c1 at location l1 on to the ferry and debark the car c1 to location l1 from the ferry\" can be removed from the plan and still have a valid plan. **Final Answer**: Yes."
|
| 10 |
+
- context: 'There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l1-0 and l1-1 are in c1; l0-1 and l0-0 are in
|
| 11 |
+
c0. Currently, p3, p2, and p1 are at l1-0, p0 and t1 are at l1-1, a0 is at l0-0, t0 is at l0-1. The goal is to reach a state where the following facts hold: p0 is at l0-0, p2 is at l1-0, p3 is at l0-1, and p1 is at l1-0. Given the plan: \"load the object p0 from location l1-1 into the truck t1, navigate the truck t1 which is in location l1-1 in city c1 to another location l1-0 in the same city, offload the object p0 from the truck t1 at location l1-0, drive truck t1 from location l1-0 in city c1 to location l1-1 in the same city, fly airplane a0 from airport l0-0 to airport l1-0, load the object p0 from location l1-0 onto the airplane a0, load the object p3 from location l1-0 onto the airplane a0, fly the airplane a0 from airport l1-0 to airport l0-0, unload the object p0 from the airplane a0 at location l0-0, remove the object p3 from the airplane a0 and place it on the location l0-0, drive the truck t0 in city c0 from location l0-1 to location l0-0, place the object p3 into the truck t0 at location l0-0, navigate the truck t0 from location l0-0 in city c0 to location l0-1 in the same city, unload object p3 from truck t0 at location l0-1\"'
|
| 12 |
+
question: 'can the following pair of consecutive actions be removed from this plan and still have a valid plan: place the object p0 onto the airplane a0 at location l1-0 and place the object p3 onto the airplane a0 at location l1-0?'
|
| 13 |
+
answer: "Let's think step by step. Step 1: A valid plan is a sequence of actions that is exectuable from the current state and achieves the goal. Step 2: Consecutive actions \"place the object p0 onto the airplane a0 at location l1-0 and place the object p3 onto the airplane a0 at location l1-0\" cannot be removed from the plan and still have a valid plan. **Final Answer**: No."
|
lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/land.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_land_bool
|
| 2 |
+
dataset_name: acp_land_bool
|
| 3 |
+
include: _boolq_cot_2shot_yaml
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: 'This is a ferry domain, where the task is to transport cars from their start to their goal locations, using a ferry. Each location is accessible by ferry from each other location. The cars can be debarked or boarded, and the ferry can carry only one car at a time. There are 2 locations and 5 cars, numbered consecutively. Currently, the ferry is at l1 location and it is empty. The cars are at locations as follows: c4 and c2 are at l1; c0, c1, and c3 are at l0. The goal is to reach a state where the following facts hold: Car c4 is at location l0, Car c0 is at location l1, Car c1 is at location l0, Car c3 is at location l0, and Car c2 is at location l1.'
|
| 8 |
+
question: 'Is the following fact a landmark (must hold at some point along any plan) for the current state? The ferry is at l0 location.'
|
| 9 |
+
answer: "Let's think step by step. Step 1: A fact is a landmark if it must hold at some point along any plan. Step 2: The fact \"The ferry is at l0 location.\" can be found by a simple procedure that traces back such atoms from the goal. **Final Answer**: Yes."
|
| 10 |
+
- context: 'There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l1-1 and l1-0 are in c1; l0-0 and l0-1 are in c0. Currently, t0 is at l0-0, t1, a0, p1, and p2 are at l1-0, p3 is in t1, p0 is in a0. The goal is to reach a state where the following facts hold: p2 is at l1-0, p0 is at l0-0, p3 is at l0-1, and p1 is at l1-0.'
|
| 11 |
+
question: 'Is the following fact a landmark (must hold at some point along any plan) for the current state? p2 is in a0.'
|
| 12 |
+
answer: "Let's think step by step. Step 1: A fact is a landmark if it must hold at some point along any plan. Step 2: The fact \"p2 is in a0.\" is not achieved
|
| 13 |
+
along the following plan: remove the object p3 from the truck t1 and place it on the location l1-0, load the object p3 from location l1-0 into the airplane a0, load the object p2 from location l1-0 into the airplane a0, remove the object p2 from the airplane a0 and place it on the location l1-0, fly the airplane a0 from the airport l1-0 to the airport l0-0, unload the object p3 from the airplane a0 at location l0-0, remove the object p0 from the airplane a0 and place it on the location l0-0, place the object p3 into the truck t0 at location l0-0. drive the truck t0 in city c0 from location l0-0 to location l0-1, unload the object p3 from the truck t0 at location l0-1. **Final Answer**: No."
|
lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/val.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_val_bool
|
| 2 |
+
dataset_name: acp_val_bool
|
| 3 |
+
include: _boolq_cot_2shot_yaml
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: 'This is a ferry domain, where the task is to transport cars from their start to their goal locations, using a ferry. Each location is accessible by ferry from each other location. The cars can be debarked or boarded, and the ferry can carry only one car at a time. There are 2 locations and 2 cars, numbered consecutively. Currently, the ferry is at l0 location and it is empty. The cars are at locations as follows: c0 and c1 are at l0. The goal is to reach a state where the following facts hold: Car c1 is at location l1 and Car c0 is at location l1.'
|
| 8 |
+
question: 'Is the following sequence of actions \"embark the car c0 at location l0 on to the ferry travel by sea from location l0 to location l1 debark the car c0 to location l1 from the ferry sail from location l1 to location l0 board car c1 at location l0 travel by sea from location l0 to location l1 sail from location l1 to location l0\" a plan for the current state?'
|
| 9 |
+
answer: "Let's think step by step. Step 1: For a sequence of actions to be a plan, it should be applicable in the current state, all actions should be valid, and the resulting state should entail the goal. Step 2: All the actions in the sequence are valid in this problem. Step 3: The action sequence is applicable in the current state. Step 4: However, the following goal fact(s) are not achieved: Car c1 is at location l1. **Final Answer**: No."
|
| 10 |
+
- context: 'This is a blocksworld domain where blocks can be placed on top of each other or on the table. There is one robotic arm that can move the block. There are 3 blocks. Currently, the robotic arm is empty. The following block(s) are on the table: block_1 and block_2. The following block(s) is stacked on top of another block: block_3 is on block_2. The goal is to reach a state where the following facts hold: The block block_1 is currently situated above the block block_2 and The block block_3 is on top of block block_1.'
|
| 11 |
+
question: 'Is the following sequence of actions "unstack the object block_3 from the object block_2 place the object block_3 on top of the object block_2 unstack object block_3 from object block_2 put down object block_3 collect the object block_3 from the table place the object block_3 on the table pick up object block_2 from the table place the object block_2 on the table pick up the object block_1 from the table stack the object block_1 on top of the object block_2 pick up object block_3 from the table stack object block_3 on top of object block_1" a plan for the current state? '
|
| 12 |
+
answer: "Let's think step by step. Step 1: For a sequence of actions to be a plan, it should be applicable in the current state, all actions should be valid, and the resulting state should entail the goal. Step 2: All the actions in the sequence are valid in this problem. Step 3: The action sequence is applicable in the current state. Step 4: The action sequence achieves the goal. **Final Answer**: Yes."
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/_gen_yaml_2shot
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- acp_gen_2shot
|
| 3 |
+
- acp_bench_hard
|
| 4 |
+
dataset_path: ibm-research/acp_bench
|
| 5 |
+
test_split: test
|
| 6 |
+
doc_to_target: "{{answer}}"
|
| 7 |
+
output_type: generate_until
|
| 8 |
+
num_fewshot: 2
|
| 9 |
+
generation_kwargs:
|
| 10 |
+
until:
|
| 11 |
+
- "\n\n\n\n"
|
| 12 |
+
- "\n\n"
|
| 13 |
+
- "**Question**:"
|
| 14 |
+
- "**Question:**"
|
| 15 |
+
- "Q:"
|
| 16 |
+
do_sample: false
|
| 17 |
+
max_gen_toks: 1000
|
| 18 |
+
temperature: 0.0
|
| 19 |
+
metadata:
|
| 20 |
+
version: 1.0
|
| 21 |
+
process_results: !function acp_utils.process_acp_results
|
| 22 |
+
metric_list:
|
| 23 |
+
- metric: "score"
|
| 24 |
+
aggregation: mean
|
| 25 |
+
higher_is_better: True
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_grammar.lark
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
NAME: /[a-zA-Z][a-zA-Z0-9-_]*/
|
| 2 |
+
LPAR : "("
|
| 3 |
+
RPAR : ")"
|
| 4 |
+
LSPAR: "["
|
| 5 |
+
RSPAR: "]"
|
| 6 |
+
COMMA: ","
|
| 7 |
+
WS: /[ \n]/
|
| 8 |
+
|
| 9 |
+
action_none : "None"
|
| 10 |
+
|
| 11 |
+
action_name : LPAR NAME (WS NAME)* RPAR
|
| 12 |
+
|
| 13 |
+
action_list : (action_name WS?)*
|
| 14 |
+
|
| 15 |
+
prog_list : action_name* (COMMA action_name)*
|
| 16 |
+
|
| 17 |
+
progression_list : LSPAR prog_list RSPAR LSPAR prog_list RSPAR
|
| 18 |
+
|
| 19 |
+
act : action_name | action_none
|
| 20 |
+
|
| 21 |
+
index: /[0-9]+[0-9]*/
|
| 22 |
+
|
| 23 |
+
start: action_list
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_utils.py
ADDED
|
@@ -0,0 +1,1128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from abc import ABC, abstractmethod
|
| 4 |
+
from collections import defaultdict
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
from lm_eval.api.registry import register_filter
|
| 8 |
+
from lm_eval.filters.extraction import RegexFilter
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
import tempfile
|
| 13 |
+
|
| 14 |
+
import tarski
|
| 15 |
+
from kstar_planner import planners as kp
|
| 16 |
+
from lark import Lark
|
| 17 |
+
from lark.lexer import Token
|
| 18 |
+
from lark.visitors import Visitor
|
| 19 |
+
from pddl.core import Problem
|
| 20 |
+
from pddl.parser.domain import DomainParser
|
| 21 |
+
from pddl.parser.problem import ProblemParser
|
| 22 |
+
from tarski.grounding.common import StateVariableLite
|
| 23 |
+
from tarski.grounding.lp_grounding import LPGroundingStrategy
|
| 24 |
+
from tarski.io import PDDLReader
|
| 25 |
+
from tarski.io import fstrips as iofs
|
| 26 |
+
from tarski.syntax.formulas import is_atom
|
| 27 |
+
from tarski.syntax.transform.action_grounding import (
|
| 28 |
+
ground_schema_into_plain_operator_from_grounding,
|
| 29 |
+
)
|
| 30 |
+
from tarski.util import SymbolIndex
|
| 31 |
+
except ModuleNotFoundError:
|
| 32 |
+
raise ModuleNotFoundError(
|
| 33 |
+
"`lark>=1.1.9`, `tarski[clingo]==0.8.2`, `pddl==0.4.2` and `kstar-planner==1.4.2` are required for evaluating the generative tasks. \
|
| 34 |
+
Please install via pip install lm-eval[acpbench] or pip install -e .[acpbench]",
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
#########################################################################
|
| 39 |
+
# Grammar
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
GRAMMAR_FILE = os.path.join(
|
| 43 |
+
os.path.dirname(os.path.abspath(__file__)), "acp_grammar.lark"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class ACPBench_Visitor(Visitor):
|
| 48 |
+
def __init__(self) -> None:
|
| 49 |
+
super().__init__()
|
| 50 |
+
self.action_lists = None
|
| 51 |
+
self.action_names = None
|
| 52 |
+
self.progression_lists = None
|
| 53 |
+
self.prog_lists = None
|
| 54 |
+
self.indexes = None
|
| 55 |
+
|
| 56 |
+
def action_list(self, tree):
|
| 57 |
+
self.action_lists = []
|
| 58 |
+
|
| 59 |
+
def prog_list(self, tree):
|
| 60 |
+
if self.prog_lists is not None:
|
| 61 |
+
self.progression_lists.append(self.prog_lists)
|
| 62 |
+
self.prog_lists = []
|
| 63 |
+
|
| 64 |
+
def progression_list(self, tree):
|
| 65 |
+
self.progression_lists = []
|
| 66 |
+
|
| 67 |
+
def action_none(self, tree):
|
| 68 |
+
self.action_names = "None"
|
| 69 |
+
|
| 70 |
+
def action_name(self, tree):
|
| 71 |
+
act_name = "(" + "".join(tree.children[1:-1]) + ")"
|
| 72 |
+
self.action_names = act_name
|
| 73 |
+
if self.action_lists is not None:
|
| 74 |
+
self.action_lists.append(act_name)
|
| 75 |
+
if self.prog_lists is not None:
|
| 76 |
+
self.prog_lists.append(act_name)
|
| 77 |
+
|
| 78 |
+
def index(self, tree):
|
| 79 |
+
self.indexes = "".join(tree.children)
|
| 80 |
+
if not self.indexes.isnumeric():
|
| 81 |
+
self.indexes = None
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class ACPGrammarParser(object):
|
| 85 |
+
def __init__(self, task) -> None:
|
| 86 |
+
self.task = task
|
| 87 |
+
with open(GRAMMAR_FILE) as f:
|
| 88 |
+
grammar = f.read()
|
| 89 |
+
self.acp_parser = Lark(grammar, start=task, parser="lalr")
|
| 90 |
+
|
| 91 |
+
def parse(self, input, debug=False):
|
| 92 |
+
def ignore_errors(e):
|
| 93 |
+
if hasattr(e, "token") and e.token.type == "$END":
|
| 94 |
+
for x in e.expected:
|
| 95 |
+
if x != "WS":
|
| 96 |
+
e.interactive_parser.feed_token(
|
| 97 |
+
Token(x, self.acp_parser.get_terminal(x).pattern.value)
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
return True
|
| 101 |
+
|
| 102 |
+
input = input.replace("\n", "")
|
| 103 |
+
input = input.strip()
|
| 104 |
+
try:
|
| 105 |
+
tree = self.acp_parser.parse(input, on_error=ignore_errors)
|
| 106 |
+
|
| 107 |
+
if debug:
|
| 108 |
+
print(tree)
|
| 109 |
+
visitor = ACPBench_Visitor()
|
| 110 |
+
visitor.visit_topdown(tree)
|
| 111 |
+
if self.task == "action_list":
|
| 112 |
+
return visitor.action_lists
|
| 113 |
+
elif self.task == "act":
|
| 114 |
+
return visitor.action_names
|
| 115 |
+
elif self.task == "action_name":
|
| 116 |
+
return visitor.action_names
|
| 117 |
+
elif self.task == "index":
|
| 118 |
+
return visitor.indexes
|
| 119 |
+
elif self.task == "progression_list":
|
| 120 |
+
if visitor.prog_lists not in visitor.progression_lists:
|
| 121 |
+
visitor.progression_lists.append(visitor.prog_lists)
|
| 122 |
+
return visitor.progression_lists
|
| 123 |
+
except Exception as e:
|
| 124 |
+
if debug:
|
| 125 |
+
print("exception")
|
| 126 |
+
print(e)
|
| 127 |
+
return None
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
##############################################################################
|
| 131 |
+
# Utils
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
# Used in next action
|
| 135 |
+
def is_on_optimal_plan(domain, problem, action, opt):
|
| 136 |
+
with (
|
| 137 |
+
tempfile.NamedTemporaryFile() as domain_temp,
|
| 138 |
+
tempfile.NamedTemporaryFile() as problem_temp,
|
| 139 |
+
):
|
| 140 |
+
with open(str(domain_temp.name), "w", encoding="utf8") as file:
|
| 141 |
+
file.write(domain.lower())
|
| 142 |
+
with open(str(problem_temp.name), "w", encoding="utf8") as file:
|
| 143 |
+
file.write(problem.lower())
|
| 144 |
+
|
| 145 |
+
# Here, we need to keep the temp files live until the end of the function
|
| 146 |
+
try:
|
| 147 |
+
P = STRIPS(str(domain_temp.name), str(problem_temp.name))
|
| 148 |
+
except Exception:
|
| 149 |
+
# Unsolvable
|
| 150 |
+
return False
|
| 151 |
+
|
| 152 |
+
a = P.get_action_or_none(action[1:-1])
|
| 153 |
+
if a is None:
|
| 154 |
+
return False
|
| 155 |
+
state = P.init
|
| 156 |
+
next_state = progress(state, a)
|
| 157 |
+
if opt is None:
|
| 158 |
+
# Get an optimal plan cost
|
| 159 |
+
plans = generate_optimal_plans_for_problem_state(
|
| 160 |
+
P, state, num_plans=1, timeout=5
|
| 161 |
+
)
|
| 162 |
+
opt = len(plans[0]["actions"])
|
| 163 |
+
else:
|
| 164 |
+
opt = int(opt)
|
| 165 |
+
|
| 166 |
+
# Getting an optimal plan for the next state
|
| 167 |
+
next_plans = generate_optimal_plans_for_problem_state(
|
| 168 |
+
P, next_state, num_plans=1, timeout=5
|
| 169 |
+
)
|
| 170 |
+
if next_plans is None:
|
| 171 |
+
return False
|
| 172 |
+
next_opt = len(next_plans[0]["actions"])
|
| 173 |
+
return next_opt + 1 == opt
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
# Used in justification
|
| 177 |
+
def is_plan(domain, problem, new_plan):
|
| 178 |
+
P = get_STRIPS(domain, problem)
|
| 179 |
+
if P is None:
|
| 180 |
+
# Unsolvable
|
| 181 |
+
return False
|
| 182 |
+
|
| 183 |
+
# Check if new_plan is a plan
|
| 184 |
+
current_state = P.init
|
| 185 |
+
for action in new_plan:
|
| 186 |
+
applicable_actions = P.get_applicable_actions(current_state)
|
| 187 |
+
app_actions_list = [f"({a.name.lower()})" for a in applicable_actions]
|
| 188 |
+
if action.lower() not in app_actions_list:
|
| 189 |
+
return False
|
| 190 |
+
a = applicable_actions[app_actions_list.index(action.lower())]
|
| 191 |
+
current_state = progress(current_state, a)
|
| 192 |
+
return entails(current_state, P.goal)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
# Used in action reachability
|
| 196 |
+
def get_action_preconditions(domain, problem, action):
|
| 197 |
+
P = get_STRIPS(domain, problem)
|
| 198 |
+
|
| 199 |
+
assert P is not None, f"Domain\n{domain}\nProblem\n{problem}\nAction: {action}"
|
| 200 |
+
a = P.get_action_or_none(action[1:-1])
|
| 201 |
+
if a is None:
|
| 202 |
+
return a
|
| 203 |
+
|
| 204 |
+
return [f"({f})" for f in a.pres]
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def generate_optimal_plans_for_problem_state(P, state, num_plans, timeout):
|
| 208 |
+
import tempfile
|
| 209 |
+
|
| 210 |
+
with (
|
| 211 |
+
tempfile.NamedTemporaryFile() as domain_temp,
|
| 212 |
+
tempfile.NamedTemporaryFile() as problem_temp,
|
| 213 |
+
):
|
| 214 |
+
create_tmp_dom_prob_replace_init(P, state, domain_temp, problem_temp)
|
| 215 |
+
plans = generate_top_q_plans(
|
| 216 |
+
domain=str(domain_temp.name),
|
| 217 |
+
problem=str(problem_temp.name),
|
| 218 |
+
num_plans=num_plans,
|
| 219 |
+
quality_bound=1.0,
|
| 220 |
+
timeout=timeout,
|
| 221 |
+
)
|
| 222 |
+
# print(plans)
|
| 223 |
+
if plans is None or len(plans["plans"]) == 0:
|
| 224 |
+
return None
|
| 225 |
+
return plans["plans"]
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def generate_top_q_plans(domain, problem, num_plans=10, quality_bound=1.0, timeout=30):
|
| 229 |
+
# print("Running K* planner")
|
| 230 |
+
plans = kp.plan_unordered_topq(
|
| 231 |
+
domain_file=Path(domain),
|
| 232 |
+
problem_file=Path(problem),
|
| 233 |
+
number_of_plans_bound=num_plans,
|
| 234 |
+
quality_bound=quality_bound,
|
| 235 |
+
timeout=timeout,
|
| 236 |
+
)
|
| 237 |
+
return plans
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
# Used in (action) reachability
|
| 241 |
+
def is_unsolvable_new_goal(domain, problem, new_goal):
|
| 242 |
+
goal = extract_goal(problem)
|
| 243 |
+
new_problem = problem.replace(goal, f"(:goal {new_goal} )")
|
| 244 |
+
return is_unsolvable(domain, new_problem)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def is_unsolvable(domain, problem):
|
| 248 |
+
with (
|
| 249 |
+
tempfile.NamedTemporaryFile() as domain_temp,
|
| 250 |
+
tempfile.NamedTemporaryFile() as problem_temp,
|
| 251 |
+
):
|
| 252 |
+
with open(str(domain_temp.name), "w", encoding="utf8") as file:
|
| 253 |
+
file.write(str(domain))
|
| 254 |
+
with open(str(problem_temp.name), "w", encoding="utf8") as file:
|
| 255 |
+
file.write(str(problem))
|
| 256 |
+
|
| 257 |
+
plans = kp.plan_unordered_topq(
|
| 258 |
+
domain_file=Path(str(domain_temp.name)),
|
| 259 |
+
problem_file=Path(str(problem_temp.name)),
|
| 260 |
+
quality_bound=1.0,
|
| 261 |
+
number_of_plans_bound=1,
|
| 262 |
+
timeout=3,
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
if len(plans["planner_error"]) > 0:
|
| 266 |
+
fl = plans["planner_error"].split("\n")[0]
|
| 267 |
+
print(f"Planner error: {fl}")
|
| 268 |
+
return False
|
| 269 |
+
if plans is None or len(plans["plans"]) == 0:
|
| 270 |
+
return plans["unsolvable"]
|
| 271 |
+
return False
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def extract_goal(prob):
|
| 275 |
+
a = prob.split("(:goal")[1]
|
| 276 |
+
cp = 1
|
| 277 |
+
for i, c in enumerate(a):
|
| 278 |
+
if c == ")":
|
| 279 |
+
cp -= 1
|
| 280 |
+
if c == "(":
|
| 281 |
+
cp += 1
|
| 282 |
+
if cp == 0:
|
| 283 |
+
return "(:goal" + a[: i + 1]
|
| 284 |
+
|
| 285 |
+
assert False
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def entails(state, partialstate):
|
| 289 |
+
return partialstate <= state
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def progress(state, act):
|
| 293 |
+
assert entails(state, act.pres), (
|
| 294 |
+
"Cannot progress with inconsistent state / action precondition:\n\t Action: "
|
| 295 |
+
+ act.name
|
| 296 |
+
+ "\n\t State: \n\t\t"
|
| 297 |
+
+ "\n\t\t".join(state)
|
| 298 |
+
)
|
| 299 |
+
return (state - act.dels) | act.adds
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def regress(state, act):
|
| 303 |
+
assert len(state & act.dels) == 0, (
|
| 304 |
+
"Cannot regress with inconsistent state / action delete effect:\n\t Action: "
|
| 305 |
+
+ act.name
|
| 306 |
+
+ "\n\t State: \n\t\t"
|
| 307 |
+
+ "\n\t\t".join(state)
|
| 308 |
+
)
|
| 309 |
+
return (state - act.adds) | act.pres
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def get_STRIPS(domain, problem):
|
| 313 |
+
with (
|
| 314 |
+
tempfile.NamedTemporaryFile() as domain_temp,
|
| 315 |
+
tempfile.NamedTemporaryFile() as problem_temp,
|
| 316 |
+
):
|
| 317 |
+
with open(str(domain_temp.name), "w", encoding="utf8") as file:
|
| 318 |
+
file.write(domain.lower())
|
| 319 |
+
with open(str(problem_temp.name), "w", encoding="utf8") as file:
|
| 320 |
+
file.write(problem.lower())
|
| 321 |
+
|
| 322 |
+
try:
|
| 323 |
+
P = STRIPS(str(domain_temp.name), str(problem_temp.name))
|
| 324 |
+
return P
|
| 325 |
+
except Exception as e:
|
| 326 |
+
print(f"||{e}||")
|
| 327 |
+
return None
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def create_tmp_dom_prob_replace_init(P, state, result_domain_file, result_problem_file):
|
| 331 |
+
d, p = P.PDDL_replace_init_pddl_parser(state)
|
| 332 |
+
with open(str(result_domain_file.name), "w", encoding="utf8") as file:
|
| 333 |
+
file.write(str(d))
|
| 334 |
+
with open(str(result_problem_file.name), "w", encoding="utf8") as file:
|
| 335 |
+
file.write(str(p))
|
| 336 |
+
|
| 337 |
+
return d, p
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def fix_name(s):
|
| 341 |
+
# (act param)
|
| 342 |
+
if "(" == s[0] and ")" == s[-1]:
|
| 343 |
+
return s[1:-1]
|
| 344 |
+
# make it space separated
|
| 345 |
+
s = s.replace(", ", " ").replace(",", " ")
|
| 346 |
+
# act(param)
|
| 347 |
+
if "(" in s:
|
| 348 |
+
assert ")" == s[-1], f"Broken name? {s}"
|
| 349 |
+
s = s.replace("(", " ").replace(")", "")
|
| 350 |
+
# act param
|
| 351 |
+
return s
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def get_atoms_pddl(d, p, atoms):
|
| 355 |
+
objs = set()
|
| 356 |
+
preds = defaultdict(list)
|
| 357 |
+
for atom in atoms:
|
| 358 |
+
a = atom.lower().strip().split(" ")
|
| 359 |
+
args = a[1:]
|
| 360 |
+
preds[a[0]].append(args)
|
| 361 |
+
objs |= set(args)
|
| 362 |
+
|
| 363 |
+
constants = [o for o in p.objects | d.constants if o.name.lower() in objs]
|
| 364 |
+
constants_dict = {}
|
| 365 |
+
for c in constants:
|
| 366 |
+
constants_dict[c.name.lower()] = c
|
| 367 |
+
assert len(objs) == len(constants), (
|
| 368 |
+
f"Could not identify all objects: {objs - set(constants_dict.keys())} not found, {set(constants_dict.keys()) - objs} should not be there"
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
state = []
|
| 372 |
+
covered_preds = set()
|
| 373 |
+
for f in d.predicates:
|
| 374 |
+
name = f.name.lower()
|
| 375 |
+
if name in preds:
|
| 376 |
+
covered_preds.add(name)
|
| 377 |
+
assert len(preds[name][0]) == f.arity, (
|
| 378 |
+
f"The arity does not match: {preds[name]} vs {f.terms}"
|
| 379 |
+
)
|
| 380 |
+
# Going over the lists of objects, adding ground predicate for each
|
| 381 |
+
for ob in preds[name]:
|
| 382 |
+
c = [constants_dict[o] for o in ob]
|
| 383 |
+
state.append(f(*c))
|
| 384 |
+
assert len(covered_preds) == len(preds.keys()), (
|
| 385 |
+
f"Covered predicates: \n{sorted(list(covered_preds))} vs \n{sorted(list(preds.keys()))}"
|
| 386 |
+
)
|
| 387 |
+
return set(state)
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
class Action:
|
| 391 |
+
def __init__(self, name, pre, add, delete):
|
| 392 |
+
self.name = name
|
| 393 |
+
self.pres = pre
|
| 394 |
+
self.adds = add
|
| 395 |
+
self.dels = delete
|
| 396 |
+
|
| 397 |
+
def __str__(self):
|
| 398 |
+
pres = "{" + ", ".join([f"({a})" for a in self.pres]) + "}"
|
| 399 |
+
adds = "{" + ", ".join([f"({a})" for a in self.adds]) + "}"
|
| 400 |
+
dels = "{" + ", ".join([f"({a})" for a in self.dels]) + "}"
|
| 401 |
+
|
| 402 |
+
return f"< {self.name}, {pres}, {adds}, {dels} >"
|
| 403 |
+
|
| 404 |
+
def toJSON(self):
|
| 405 |
+
return json.dumps(
|
| 406 |
+
{
|
| 407 |
+
"name": self.name,
|
| 408 |
+
"preconditions": [f"({a})" for a in self.pres],
|
| 409 |
+
"add_effects": [f"({a})" for a in self.adds],
|
| 410 |
+
"delete_effects": [f"({a})" for a in self.dels],
|
| 411 |
+
},
|
| 412 |
+
sort_keys=True,
|
| 413 |
+
indent=4,
|
| 414 |
+
)
|
| 415 |
+
|
| 416 |
+
def __repr__(self):
|
| 417 |
+
return self.name
|
| 418 |
+
|
| 419 |
+
def __eq__(self, action):
|
| 420 |
+
return self.name == action.name
|
| 421 |
+
|
| 422 |
+
def __hash__(self):
|
| 423 |
+
return hash(self.name)
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
class STRIPS:
|
| 427 |
+
def __init__(self, domain, problem):
|
| 428 |
+
self.domain_file = domain
|
| 429 |
+
self.problem_file = problem
|
| 430 |
+
self.reader = PDDLReader(raise_on_error=True)
|
| 431 |
+
self.reader.parse_domain(domain)
|
| 432 |
+
self.problem = self.reader.parse_instance(problem)
|
| 433 |
+
(self.grounded_fluents, init, goal, self.operators, self.grounder) = (
|
| 434 |
+
self.ground_problem(self.problem)
|
| 435 |
+
)
|
| 436 |
+
|
| 437 |
+
self.fluents = set([fix_name(str(f)) for f in self.grounded_fluents])
|
| 438 |
+
self.fluents_map = dict()
|
| 439 |
+
for f in self.grounded_fluents:
|
| 440 |
+
self.fluents_map[fix_name(str(f))] = f
|
| 441 |
+
self.init = set([fix_name(str(f)) for f in init])
|
| 442 |
+
self.goal = set([fix_name(str(f)) for f in goal])
|
| 443 |
+
self.actions = set()
|
| 444 |
+
self.action_map = {}
|
| 445 |
+
self.init_fluents = [self.fluents_map[f] for f in self.init]
|
| 446 |
+
|
| 447 |
+
self.static_predicates = [i.name for i in self.grounder.static_symbols]
|
| 448 |
+
for op in self.operators:
|
| 449 |
+
act = self.operator_to_action(op)
|
| 450 |
+
self.actions.add(act)
|
| 451 |
+
self.action_map[act.name.lower()] = act
|
| 452 |
+
|
| 453 |
+
def __str__(self):
|
| 454 |
+
fluents = "P = {" + ", ".join([f"({a})" for a in self.fluents]) + "}"
|
| 455 |
+
init = "I = {" + ", ".join([f"({a})" for a in self.init]) + "}"
|
| 456 |
+
goal = "G = {" + ", ".join([f"({a})" for a in self.goal]) + "}"
|
| 457 |
+
actions = "A = {" + "\n ".join([a.__str__() for a in self.actions]) + "}"
|
| 458 |
+
return fluents + ",\n" + init + "\n" + goal + "\n" + actions
|
| 459 |
+
|
| 460 |
+
def toJSON(self):
|
| 461 |
+
actions = [a.toJSON() for a in self.actions]
|
| 462 |
+
return json.dumps(
|
| 463 |
+
{
|
| 464 |
+
"fluents": list(self.fluents),
|
| 465 |
+
"initial_state": list(self.init),
|
| 466 |
+
"goal": list(self.goal),
|
| 467 |
+
"actions": actions,
|
| 468 |
+
},
|
| 469 |
+
sort_keys=True,
|
| 470 |
+
indent=4,
|
| 471 |
+
)
|
| 472 |
+
|
| 473 |
+
def operator_to_action(self, op, check_fluents=True, check_static=False):
|
| 474 |
+
adds = {
|
| 475 |
+
fix_name(str(f.atom)) for f in op.effects if isinstance(f, iofs.AddEffect)
|
| 476 |
+
} & self.fluents
|
| 477 |
+
dels = {
|
| 478 |
+
fix_name(str(f.atom)) for f in op.effects if isinstance(f, iofs.DelEffect)
|
| 479 |
+
} & self.fluents
|
| 480 |
+
pre = self.fix_pre_name(op.precondition)
|
| 481 |
+
if check_fluents:
|
| 482 |
+
pre = pre & self.fluents
|
| 483 |
+
if check_static:
|
| 484 |
+
pre = {p for p in pre if p.split()[0] not in self.static_predicates}
|
| 485 |
+
act = Action(fix_name(str(op)), pre, adds, dels)
|
| 486 |
+
return act
|
| 487 |
+
|
| 488 |
+
def fix_pre_name(self, precondition):
|
| 489 |
+
if not is_atom(precondition):
|
| 490 |
+
return {fix_name(str(f)) for f in precondition.subformulas}
|
| 491 |
+
return {fix_name(str(precondition))}
|
| 492 |
+
|
| 493 |
+
def action(self, name):
|
| 494 |
+
return self.action_map[fix_name(name).lower()]
|
| 495 |
+
|
| 496 |
+
def get_action_or_none(self, name):
|
| 497 |
+
if "(" in name and ")" != name[-1]:
|
| 498 |
+
return None
|
| 499 |
+
return self.action_map.get(fix_name(name).lower(), None)
|
| 500 |
+
|
| 501 |
+
def fluent(self, name):
|
| 502 |
+
return fix_name(name)
|
| 503 |
+
|
| 504 |
+
def static_symbols(self):
|
| 505 |
+
return list(self.grounder.static_symbols)
|
| 506 |
+
|
| 507 |
+
def fluent_symbols(self):
|
| 508 |
+
return list(self.grounder.fluent_symbols)
|
| 509 |
+
|
| 510 |
+
def get_grounded_atoms(self, symbol):
|
| 511 |
+
variables = SymbolIndex()
|
| 512 |
+
lang = symbol.language
|
| 513 |
+
key = "atom_" + symbol.name
|
| 514 |
+
model = self.grounder._solve_lp()
|
| 515 |
+
if (
|
| 516 |
+
key in model
|
| 517 |
+
): # in case there is no reachable ground state variable from that fluent symbol
|
| 518 |
+
for binding in model[key]:
|
| 519 |
+
binding_with_constants = tuple(lang.get(c) for c in binding)
|
| 520 |
+
variables.add(StateVariableLite(symbol, binding_with_constants))
|
| 521 |
+
return variables
|
| 522 |
+
|
| 523 |
+
def get_applicable_actions(self, s):
|
| 524 |
+
return [a for a in self.actions if entails(s, a.pres)]
|
| 525 |
+
|
| 526 |
+
def ground_problem(self, problem):
|
| 527 |
+
grounder = LPGroundingStrategy(problem, include_variable_inequalities=True)
|
| 528 |
+
action_groundings = grounder.ground_actions()
|
| 529 |
+
operators = []
|
| 530 |
+
for action_name, groundings in action_groundings.items():
|
| 531 |
+
action = problem.get_action(action_name)
|
| 532 |
+
for grounding in groundings:
|
| 533 |
+
operators.append(
|
| 534 |
+
ground_schema_into_plain_operator_from_grounding(action, grounding)
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
+
grounded_fluents = set(
|
| 538 |
+
[
|
| 539 |
+
grounded_fluent.to_atom()
|
| 540 |
+
for grounded_fluent in grounder.ground_state_variables().objects
|
| 541 |
+
]
|
| 542 |
+
)
|
| 543 |
+
init = [f for f in problem.init.as_atoms() if f in grounded_fluents]
|
| 544 |
+
if isinstance(problem.goal, tarski.syntax.Atom):
|
| 545 |
+
goal = [problem.goal]
|
| 546 |
+
else:
|
| 547 |
+
goal = [f for f in problem.goal.subformulas if f in grounded_fluents]
|
| 548 |
+
|
| 549 |
+
return (grounded_fluents, init, goal, operators, grounder)
|
| 550 |
+
|
| 551 |
+
def get_static(self):
|
| 552 |
+
static_symbols = self.static_symbols()
|
| 553 |
+
ret = []
|
| 554 |
+
for symbol in static_symbols:
|
| 555 |
+
ret.extend(self.get_grounded_atoms(symbol))
|
| 556 |
+
return set([fix_name(str(x)) for x in ret])
|
| 557 |
+
|
| 558 |
+
def PDDL_replace_init_pddl_parser(self, s):
|
| 559 |
+
d = DomainParser()(open(self.domain_file, "r").read().lower())
|
| 560 |
+
p = ProblemParser()(open(self.problem_file, "r").read().lower())
|
| 561 |
+
|
| 562 |
+
new_state = get_atoms_pddl(d, p, s | self.get_static())
|
| 563 |
+
|
| 564 |
+
new_p = Problem(
|
| 565 |
+
p.name, domain=d, objects=p.objects, init=new_state, goal=p.goal
|
| 566 |
+
)
|
| 567 |
+
|
| 568 |
+
return d, new_p
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
def parse_ans(response: str, parser: ACPGrammarParser, task: str):
|
| 572 |
+
return [parser.parse(clean_answer(resp, task)) for resp in response]
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
# def parse_ans(response : str, parser : ACPGrammarParser, task : str):
|
| 576 |
+
# ans = [parser.parse(clean_answer(resp, task), debug=True) for resp in response]
|
| 577 |
+
# if any(elem is None for elem in ans) or any(elem is None for elem in ans[0]):
|
| 578 |
+
# return None
|
| 579 |
+
# return ans
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def remove_garbage(s):
|
| 583 |
+
while True:
|
| 584 |
+
if s.endswith("."):
|
| 585 |
+
s = s[:-1]
|
| 586 |
+
elif s.endswith("\n"):
|
| 587 |
+
s = s[:-2]
|
| 588 |
+
else:
|
| 589 |
+
break
|
| 590 |
+
return s.rstrip()
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
def compare_str(s1, s2):
|
| 594 |
+
return remove_garbage(s1).lower() == remove_garbage(s2).lower()
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
def compare(l1, l2):
|
| 598 |
+
if not isinstance(l1, list):
|
| 599 |
+
return compare_str(l1, l2)
|
| 600 |
+
if not isinstance(l2, list):
|
| 601 |
+
return False
|
| 602 |
+
for i, v in enumerate(l1):
|
| 603 |
+
if not compare(v, l2[i]):
|
| 604 |
+
return False
|
| 605 |
+
return True
|
| 606 |
+
|
| 607 |
+
|
| 608 |
+
def check_prog_response(resp):
|
| 609 |
+
if (
|
| 610 |
+
"Positive Effects".lower() in resp.lower()
|
| 611 |
+
and "Negative Effects".lower() in resp.lower()
|
| 612 |
+
):
|
| 613 |
+
if "[" not in resp:
|
| 614 |
+
return True
|
| 615 |
+
return False
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
def clean_answer(resp, task):
|
| 619 |
+
# Minor cleanup
|
| 620 |
+
if "progression_gen" in task:
|
| 621 |
+
# Check for Positive Effects and Negative Effects instead of separation
|
| 622 |
+
if check_prog_response(resp):
|
| 623 |
+
# replace **Positive Effects** with "["
|
| 624 |
+
# replace **Negative Effects** with "] ["
|
| 625 |
+
# append "]" to the end
|
| 626 |
+
resp2 = resp.lower()
|
| 627 |
+
resp2 = resp2.replace("*", "")
|
| 628 |
+
resp2 = resp2.replace("positive effects", "[")
|
| 629 |
+
resp2 = resp2.replace("negative effects", "] [")
|
| 630 |
+
resp2 = resp2 + "]"
|
| 631 |
+
return resp2
|
| 632 |
+
if "action_justification_gen" in task:
|
| 633 |
+
# Check for "simplified plan:"
|
| 634 |
+
if "simplified plan:" in resp.lower():
|
| 635 |
+
resp2 = resp.lower()
|
| 636 |
+
resp2 = resp2.replace("*", "")
|
| 637 |
+
resp2 = resp2.split("simplified plan:")[1]
|
| 638 |
+
return resp2
|
| 639 |
+
return resp
|
| 640 |
+
|
| 641 |
+
|
| 642 |
+
def get_grammar_task(task):
|
| 643 |
+
# print(task)
|
| 644 |
+
if task == "reachable_atom_gen":
|
| 645 |
+
return "act"
|
| 646 |
+
elif task == "progression_gen":
|
| 647 |
+
return "progression_list"
|
| 648 |
+
elif task == "validation_gen":
|
| 649 |
+
return "index"
|
| 650 |
+
elif task == "reachable_action_gen":
|
| 651 |
+
return "act"
|
| 652 |
+
elif task == "action_justification_gen":
|
| 653 |
+
return "action_list"
|
| 654 |
+
elif task == "landmarks_gen":
|
| 655 |
+
return "act"
|
| 656 |
+
elif task == "goal_closer_gen":
|
| 657 |
+
return "action_name"
|
| 658 |
+
elif task == "applicable_actions_gen":
|
| 659 |
+
return "action_list"
|
| 660 |
+
|
| 661 |
+
|
| 662 |
+
##############################################################################
|
| 663 |
+
# Evaluators
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
def fix_action_name(a):
|
| 667 |
+
assert a.startswith("(") and a.endswith(")")
|
| 668 |
+
return "(" + " ".join([x.strip() for x in a[1:-1].split(" ") if len(x) > 0]) + ")"
|
| 669 |
+
|
| 670 |
+
|
| 671 |
+
def str_remove_before_first_parentheses(s):
|
| 672 |
+
if s.startswith("("):
|
| 673 |
+
return s
|
| 674 |
+
try:
|
| 675 |
+
return s[s.index("(") :]
|
| 676 |
+
except Exception:
|
| 677 |
+
return ""
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
def str_remove_after_last_parentheses(s):
|
| 681 |
+
if s.endswith(")"):
|
| 682 |
+
return s
|
| 683 |
+
|
| 684 |
+
i = s.rfind(")")
|
| 685 |
+
|
| 686 |
+
if i == -1:
|
| 687 |
+
return ""
|
| 688 |
+
return s[: i + 1]
|
| 689 |
+
|
| 690 |
+
|
| 691 |
+
def cleanup_answer(ans):
|
| 692 |
+
if isinstance(ans, str):
|
| 693 |
+
ans = str_remove_before_first_parentheses(ans)
|
| 694 |
+
ans = str_remove_after_last_parentheses(ans)
|
| 695 |
+
ans = ans.lower()
|
| 696 |
+
ans = (
|
| 697 |
+
ans.replace(")\n(", ")######(")
|
| 698 |
+
.replace("),(", ")######(")
|
| 699 |
+
.replace(") (", ")######(")
|
| 700 |
+
.split("######")
|
| 701 |
+
)
|
| 702 |
+
return ans
|
| 703 |
+
if isinstance(ans, list):
|
| 704 |
+
res = []
|
| 705 |
+
for x in ans:
|
| 706 |
+
res.extend(cleanup_answer(x))
|
| 707 |
+
return res
|
| 708 |
+
|
| 709 |
+
|
| 710 |
+
def set_equal(ans1, ans2):
|
| 711 |
+
return set(ans1) == set(ans2)
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
class BaseEvaluator(ABC):
|
| 715 |
+
def __init__(self) -> None:
|
| 716 |
+
self.scores = []
|
| 717 |
+
|
| 718 |
+
@abstractmethod
|
| 719 |
+
def get_score(self, ans, doc):
|
| 720 |
+
pass
|
| 721 |
+
|
| 722 |
+
def add_scores(self, scores):
|
| 723 |
+
self.scores.extend(scores)
|
| 724 |
+
|
| 725 |
+
def get_avg_score(self):
|
| 726 |
+
avg_score = sum(self.scores) / len(self.scores)
|
| 727 |
+
return avg_score
|
| 728 |
+
|
| 729 |
+
|
| 730 |
+
def get_evaluator(group):
|
| 731 |
+
if group == "applicable_actions_gen":
|
| 732 |
+
return ApplicabilityEvaluator()
|
| 733 |
+
elif group == "progression_gen":
|
| 734 |
+
return ProgressionEvaluator()
|
| 735 |
+
elif group == "validation_gen":
|
| 736 |
+
return ValidationEvaluator()
|
| 737 |
+
elif group == "reachable_atom_gen":
|
| 738 |
+
return ReachabilityEvaluator()
|
| 739 |
+
elif group == "goal_closer_gen":
|
| 740 |
+
return NextActionEvaluator()
|
| 741 |
+
elif group == "action_justification_gen":
|
| 742 |
+
return JustificationEvaluator()
|
| 743 |
+
elif group == "landmarks_gen":
|
| 744 |
+
return LandmarksEvaluator()
|
| 745 |
+
elif group == "reachable_action_gen":
|
| 746 |
+
return ActionReachabilityEvaluator()
|
| 747 |
+
assert True, f"Group {group} not found"
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
"""
|
| 751 |
+
Action Reachability task: generate a valid action that is not applicable to any reachable state.
|
| 752 |
+
answer: A subset of actions that are known to be unreachable (not an exhaustive set).
|
| 753 |
+
It is empty only when we *know* that there are no such actions.
|
| 754 |
+
"""
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
class ActionReachabilityEvaluator(BaseEvaluator):
|
| 758 |
+
def get_score(self, ans, doc):
|
| 759 |
+
real_answer = doc["answer"]
|
| 760 |
+
if not real_answer or len(real_answer) == 0:
|
| 761 |
+
# The correct answer is None
|
| 762 |
+
self.add_scores(
|
| 763 |
+
["none" == x.strip().lower() if x is not None else False for x in ans]
|
| 764 |
+
)
|
| 765 |
+
else:
|
| 766 |
+
for x in ans:
|
| 767 |
+
if x is None:
|
| 768 |
+
self.scores.append(False)
|
| 769 |
+
continue
|
| 770 |
+
action = x.strip().lower()
|
| 771 |
+
if action in real_answer:
|
| 772 |
+
# The answer is in the subset of stored correct answers
|
| 773 |
+
self.scores.append(True)
|
| 774 |
+
continue
|
| 775 |
+
prec = get_action_preconditions(
|
| 776 |
+
doc["PDDL_domain"].lower(), doc["PDDL_problem"].lower(), action
|
| 777 |
+
)
|
| 778 |
+
if prec is None:
|
| 779 |
+
# The answer does not correspond to a valid action
|
| 780 |
+
self.scores.append(False)
|
| 781 |
+
else:
|
| 782 |
+
# Need to run a planner on a task with the answer action preconditions as the new goal
|
| 783 |
+
prec = f"(and {' '.join(prec)})"
|
| 784 |
+
self.scores.append(
|
| 785 |
+
is_unsolvable_new_goal(
|
| 786 |
+
doc["PDDL_domain"].lower(),
|
| 787 |
+
doc["PDDL_problem"].lower(),
|
| 788 |
+
prec,
|
| 789 |
+
)
|
| 790 |
+
)
|
| 791 |
+
|
| 792 |
+
return self.get_avg_score()
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
"""
|
| 796 |
+
Action Applicability task: generate all actions that are applicable in the current state.
|
| 797 |
+
answer: A set of all applicable actions.
|
| 798 |
+
"""
|
| 799 |
+
|
| 800 |
+
|
| 801 |
+
class ApplicabilityEvaluator(BaseEvaluator):
|
| 802 |
+
def get_score(self, ans, doc):
|
| 803 |
+
real_answer = doc["answer"]
|
| 804 |
+
real_answer = [a.lower() for a in real_answer]
|
| 805 |
+
ans = [[fix_action_name(a) for a in x] if x is not None else None for x in ans]
|
| 806 |
+
|
| 807 |
+
# Check if the answer is equal (as a set) to the real stored answer
|
| 808 |
+
self.add_scores(
|
| 809 |
+
[
|
| 810 |
+
set_equal(real_answer, cleanup_answer(x)) if x is not None else False
|
| 811 |
+
for x in ans
|
| 812 |
+
]
|
| 813 |
+
)
|
| 814 |
+
return self.get_avg_score()
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
def is_subsequence(plan, new_plan):
|
| 818 |
+
i = 0
|
| 819 |
+
for a in plan:
|
| 820 |
+
if a == new_plan[i]:
|
| 821 |
+
i += 1
|
| 822 |
+
if len(new_plan) == i:
|
| 823 |
+
# Done
|
| 824 |
+
return True
|
| 825 |
+
return False
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
def is_subsequence_and_plan(domain, problem, plan, new_plan):
|
| 829 |
+
if len(plan) <= len(new_plan):
|
| 830 |
+
return False
|
| 831 |
+
if not is_subsequence(plan, new_plan):
|
| 832 |
+
return False
|
| 833 |
+
return is_plan(domain, problem, new_plan)
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
"""
|
| 837 |
+
Justification task: generate a proper subsequence of the given plan that is also a plan.
|
| 838 |
+
answer: A list of examples of actions that can be removed (ignored in evaluation).
|
| 839 |
+
"""
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
class JustificationEvaluator(BaseEvaluator):
|
| 843 |
+
def get_score(self, ans, doc):
|
| 844 |
+
# Sequence of actions (plan) from the question
|
| 845 |
+
if "inputs" in doc: # old field name
|
| 846 |
+
seq = doc["inputs"][19:-147]
|
| 847 |
+
else:
|
| 848 |
+
seq = doc["question"][19:-147]
|
| 849 |
+
seq = seq.replace(") (", ")######(").split("######")
|
| 850 |
+
for x in ans:
|
| 851 |
+
if x is None:
|
| 852 |
+
self.scores.append(False)
|
| 853 |
+
continue
|
| 854 |
+
# An answer plan candidate
|
| 855 |
+
x = [fix_action_name(a) for a in x]
|
| 856 |
+
if len(x) == 0:
|
| 857 |
+
# Wrong answer - never an empty sequence
|
| 858 |
+
self.scores.append(0)
|
| 859 |
+
continue
|
| 860 |
+
# Check if the plan candidate from the answer (a) is a proper subsequence of the plan in the question and (b) is a plan.
|
| 861 |
+
self.scores.append(
|
| 862 |
+
is_subsequence_and_plan(
|
| 863 |
+
doc["PDDL_domain"].lower(), doc["PDDL_problem"].lower(), seq, x
|
| 864 |
+
)
|
| 865 |
+
)
|
| 866 |
+
return self.get_avg_score()
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
"""
|
| 870 |
+
Landmarks task: generate a fact that is a non-trivial landmark for the current state.
|
| 871 |
+
answer: A list of facts that are found to be landmarks and a list of facts that are found to be non-landmarks.
|
| 872 |
+
|
| 873 |
+
The questions are generated only for cases where all facts either
|
| 874 |
+
(a) hold in the current state,
|
| 875 |
+
(b) true in goal,
|
| 876 |
+
(c) are found to be landmarks, or
|
| 877 |
+
(d) are found to be non-landmarks.
|
| 878 |
+
In such cases, the evaluation is simple, it does not require checking whether a fact is a landmark, it was
|
| 879 |
+
already done during question generation.
|
| 880 |
+
"""
|
| 881 |
+
|
| 882 |
+
|
| 883 |
+
class LandmarksEvaluator(BaseEvaluator):
|
| 884 |
+
def get_score(self, ans, doc):
|
| 885 |
+
# The set of facts that are found to be landmarks
|
| 886 |
+
real_answer = doc["answer"]
|
| 887 |
+
real_answer_yes = [a.lower() for a in real_answer["yes"]]
|
| 888 |
+
|
| 889 |
+
for x in ans:
|
| 890 |
+
if x is None:
|
| 891 |
+
self.scores.append(False)
|
| 892 |
+
continue
|
| 893 |
+
if x.strip().lower() in real_answer_yes:
|
| 894 |
+
# The answer fact is known to be landmark
|
| 895 |
+
self.scores.append(True)
|
| 896 |
+
elif x.strip().lower() == "none":
|
| 897 |
+
# The answer is none, correct only if there are no known landmarks,
|
| 898 |
+
# since we only generate questions when that means that there are no non-trivial landmarks
|
| 899 |
+
self.scores.append(len(real_answer_yes) == 0)
|
| 900 |
+
else:
|
| 901 |
+
# All other cases the answer is incorrect
|
| 902 |
+
self.scores.append(False)
|
| 903 |
+
|
| 904 |
+
return self.get_avg_score()
|
| 905 |
+
|
| 906 |
+
|
| 907 |
+
"""
|
| 908 |
+
Next Action task: generate an action that takes us closer to the goal.
|
| 909 |
+
answer:
|
| 910 |
+
(a) A list of applicable actions that are known to be correct answers
|
| 911 |
+
(b) A list of applicable actions that are known to be incorrect answers
|
| 912 |
+
(c) The rest of the applicable actions (maybe).
|
| 913 |
+
"""
|
| 914 |
+
|
| 915 |
+
|
| 916 |
+
class NextActionEvaluator(BaseEvaluator):
|
| 917 |
+
def get_score(self, ans, doc):
|
| 918 |
+
real_answer = doc["answer"]
|
| 919 |
+
real_answer_yes = [a.lower() for a in real_answer["yes"]]
|
| 920 |
+
real_answer_no = [a.lower() for a in real_answer["no"]]
|
| 921 |
+
real_answer_maybe = [a.lower() for a in real_answer["maybe"]]
|
| 922 |
+
# The cost of the optimal plan from the current state
|
| 923 |
+
opt = real_answer.get("opt", None)
|
| 924 |
+
for x in ans:
|
| 925 |
+
if x is None:
|
| 926 |
+
self.scores.append(False)
|
| 927 |
+
continue
|
| 928 |
+
action = x.strip().lower()
|
| 929 |
+
if action in real_answer_yes:
|
| 930 |
+
# Known to be correct
|
| 931 |
+
self.scores.append(True)
|
| 932 |
+
elif action in real_answer_no:
|
| 933 |
+
# Known to be incorrect
|
| 934 |
+
self.scores.append(False)
|
| 935 |
+
elif action not in real_answer_maybe:
|
| 936 |
+
# Not applicable, must be incorrect
|
| 937 |
+
self.scores.append(False)
|
| 938 |
+
else:
|
| 939 |
+
# Unknown, need to run a planner to check whether the state that results from applying the action is closer to the goal
|
| 940 |
+
# meaning has smaller optimal plan cost.
|
| 941 |
+
self.scores.append(
|
| 942 |
+
is_on_optimal_plan(
|
| 943 |
+
doc["PDDL_domain"].lower(),
|
| 944 |
+
doc["PDDL_problem"].lower(),
|
| 945 |
+
action,
|
| 946 |
+
opt,
|
| 947 |
+
)
|
| 948 |
+
)
|
| 949 |
+
|
| 950 |
+
return self.get_avg_score()
|
| 951 |
+
|
| 952 |
+
|
| 953 |
+
"""
|
| 954 |
+
Progression task: generate the positive and negative effects of an action in the current state.
|
| 955 |
+
answer:
|
| 956 |
+
(a) A list of facts that were false and become true, when the action is applied
|
| 957 |
+
(b) A list of facts that were true and become false, when the action is applied
|
| 958 |
+
"""
|
| 959 |
+
|
| 960 |
+
|
| 961 |
+
class ProgressionEvaluator(BaseEvaluator):
|
| 962 |
+
def get_score(self, ans, doc):
|
| 963 |
+
real_answer = doc["answer"]
|
| 964 |
+
real_answer_pos = [a.lower() for a in real_answer["pos"]]
|
| 965 |
+
real_answer_neg = [a.lower() for a in real_answer["neg"]]
|
| 966 |
+
|
| 967 |
+
for x in ans:
|
| 968 |
+
# The answer should be two lists. We allow for a single list and assume that the second one is empty (relaxed evaluation).
|
| 969 |
+
if x is None or len(x) > 2 or len(x) < 1:
|
| 970 |
+
self.scores.append(False)
|
| 971 |
+
else:
|
| 972 |
+
p = cleanup_answer(x[0])
|
| 973 |
+
if len(x) == 2:
|
| 974 |
+
n = cleanup_answer(x[1])
|
| 975 |
+
else:
|
| 976 |
+
# Assuming the last element is dropped because it is empty
|
| 977 |
+
n = []
|
| 978 |
+
# Check if the answer is equal as sets to the correct answers.
|
| 979 |
+
ans = [set_equal(real_answer_pos, p), set_equal(real_answer_neg, n)]
|
| 980 |
+
self.scores.append(all(ans))
|
| 981 |
+
|
| 982 |
+
return self.get_avg_score()
|
| 983 |
+
|
| 984 |
+
|
| 985 |
+
"""
|
| 986 |
+
Reachability task: generate a valid fact that will never become true in any reachable state.
|
| 987 |
+
answer: A subset of facts that are known to be unreachable (not an exhaustive set).
|
| 988 |
+
It is empty only when we *know* that there are no such facts.
|
| 989 |
+
"""
|
| 990 |
+
|
| 991 |
+
|
| 992 |
+
class ReachabilityEvaluator(BaseEvaluator):
|
| 993 |
+
def get_score(self, ans, doc):
|
| 994 |
+
real_answer = doc["answer"]
|
| 995 |
+
real_answer = [f"({x.strip().lower()})" for x in real_answer]
|
| 996 |
+
|
| 997 |
+
if len(real_answer) == 0:
|
| 998 |
+
# The correct answer is None
|
| 999 |
+
self.add_scores(
|
| 1000 |
+
["none" == x.strip().lower() if x is not None else False for x in ans]
|
| 1001 |
+
)
|
| 1002 |
+
else:
|
| 1003 |
+
for x in ans:
|
| 1004 |
+
if x is None:
|
| 1005 |
+
self.scores.append(False)
|
| 1006 |
+
elif x.strip().lower() in real_answer:
|
| 1007 |
+
# The answer is in the subset of stored correct answers
|
| 1008 |
+
self.scores.append(True)
|
| 1009 |
+
else:
|
| 1010 |
+
# Need to run a planner on a task with the answer fact as the new goal
|
| 1011 |
+
atom = x.strip().lower()
|
| 1012 |
+
self.scores.append(
|
| 1013 |
+
is_unsolvable_new_goal(
|
| 1014 |
+
doc["PDDL_domain"].lower(),
|
| 1015 |
+
doc["PDDL_problem"].lower(),
|
| 1016 |
+
atom,
|
| 1017 |
+
)
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
return self.get_avg_score()
|
| 1021 |
+
|
| 1022 |
+
|
| 1023 |
+
"""
|
| 1024 |
+
Validation task: generate an index of the first inapplicable action in the given sequence.
|
| 1025 |
+
answer: the correct index.
|
| 1026 |
+
"""
|
| 1027 |
+
|
| 1028 |
+
|
| 1029 |
+
class ValidationEvaluator(BaseEvaluator):
|
| 1030 |
+
def get_score(self, ans, doc):
|
| 1031 |
+
real_answer = str(doc["answer"])
|
| 1032 |
+
assert int(real_answer) >= 0, (
|
| 1033 |
+
f"The index must be non-negative, received {real_answer}"
|
| 1034 |
+
)
|
| 1035 |
+
# Exact match
|
| 1036 |
+
self.add_scores(
|
| 1037 |
+
[
|
| 1038 |
+
real_answer.lower() == x.strip().lower() if x is not None else False
|
| 1039 |
+
for x in ans
|
| 1040 |
+
]
|
| 1041 |
+
)
|
| 1042 |
+
|
| 1043 |
+
return self.get_avg_score()
|
| 1044 |
+
|
| 1045 |
+
|
| 1046 |
+
##############################################################################
|
| 1047 |
+
|
| 1048 |
+
|
| 1049 |
+
def dump_item(item, **kwargs):
|
| 1050 |
+
return json.dumps(item)
|
| 1051 |
+
|
| 1052 |
+
|
| 1053 |
+
def parse_prediction(prediction):
|
| 1054 |
+
try:
|
| 1055 |
+
ans = json.loads(prediction.strip())
|
| 1056 |
+
response = ans.get("answer", None)
|
| 1057 |
+
return response
|
| 1058 |
+
except Exception as e:
|
| 1059 |
+
print(f"Exception occurred {e}")
|
| 1060 |
+
return prediction
|
| 1061 |
+
|
| 1062 |
+
|
| 1063 |
+
@register_filter("ACP_grammar_filter")
|
| 1064 |
+
class ACPGrammarFilter(RegexFilter):
|
| 1065 |
+
"""Filtering Index using"""
|
| 1066 |
+
|
| 1067 |
+
def __init__(self, *args, **kwargs):
|
| 1068 |
+
self.parser = ACPGrammarParser(kwargs["grammar_task"])
|
| 1069 |
+
self.clean = kwargs["clean"] if "clean" in kwargs else None
|
| 1070 |
+
|
| 1071 |
+
def clean_pos_neg(self, resp):
|
| 1072 |
+
# Check for Positive Effects and Negative Effects instead of separation
|
| 1073 |
+
if check_prog_response(resp):
|
| 1074 |
+
resp2 = resp.lower()
|
| 1075 |
+
resp2 = resp2.replace("*", "")
|
| 1076 |
+
resp2 = resp2.replace("positive effects", "[")
|
| 1077 |
+
resp2 = resp2.replace("negative effects", "] [")
|
| 1078 |
+
resp2 = resp2 + "]"
|
| 1079 |
+
return resp2
|
| 1080 |
+
return resp
|
| 1081 |
+
|
| 1082 |
+
def clean_simplified_plan(self, resp):
|
| 1083 |
+
# Check for "simplified plan:"
|
| 1084 |
+
if "simplified plan:" in resp.lower():
|
| 1085 |
+
resp2 = resp.lower()
|
| 1086 |
+
resp2 = resp2.replace("*", "")
|
| 1087 |
+
resp2 = resp2.split("simplified plan:")[1]
|
| 1088 |
+
return resp2
|
| 1089 |
+
return resp
|
| 1090 |
+
|
| 1091 |
+
def apply(self, resps, docs):
|
| 1092 |
+
if self.clean == "pos_neg":
|
| 1093 |
+
filtered_resps = [
|
| 1094 |
+
[self.parser.parse(self.clean_pos_neg(r)) for r in resp]
|
| 1095 |
+
for resp in resps
|
| 1096 |
+
]
|
| 1097 |
+
elif self.clean == "simplified plan":
|
| 1098 |
+
filtered_resps = [
|
| 1099 |
+
[self.parser.parse(self.clean_simplified_plan(r)) for r in resp]
|
| 1100 |
+
for resp in resps
|
| 1101 |
+
]
|
| 1102 |
+
else:
|
| 1103 |
+
filtered_resps = [[self.parser.parse(r) for r in resp] for resp in resps]
|
| 1104 |
+
return filtered_resps
|
| 1105 |
+
|
| 1106 |
+
|
| 1107 |
+
def process_acp_results(doc, results):
|
| 1108 |
+
return {"score": get_evaluator(doc["group"]).get_score(results, doc)}
|
| 1109 |
+
|
| 1110 |
+
|
| 1111 |
+
def get_score(references, predictions, **kwargs):
|
| 1112 |
+
# print(f"References: {references}")
|
| 1113 |
+
# print(f"Predictions: {predictions}")
|
| 1114 |
+
data = json.loads(references[0].strip())
|
| 1115 |
+
real_ans = data["answer"]
|
| 1116 |
+
task = data["group"]
|
| 1117 |
+
|
| 1118 |
+
responses = [parse_prediction(prediction) for prediction in predictions]
|
| 1119 |
+
|
| 1120 |
+
print(f"Real answer: {real_ans}")
|
| 1121 |
+
print(f"Model answers: {responses}")
|
| 1122 |
+
parser = ACPGrammarParser(get_grammar_task(task))
|
| 1123 |
+
ans = parse_ans(responses, parser, task)
|
| 1124 |
+
|
| 1125 |
+
print(f"Parsed model answers: {ans}")
|
| 1126 |
+
score = get_evaluator(task).get_score(ans, data)
|
| 1127 |
+
|
| 1128 |
+
return {"get_score": score}
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/act_reach.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_areach_gen
|
| 2 |
+
dataset_name: acp_areach_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. The grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. There are 2 keys in 0 different shapes: Key key0-0 is of shape shape0, Key key0-1 is of shape shape0. Currently, the robot is at position f2-2f and its arm is empty. All the positions are open except the following: f2-0f has shape0 shaped lock, f4-2f has shape0 shaped lock. Key key0-0 is at position f1-2f. Key key0-1 is at position f1-3f. The available actions are: (unlock ?curpos ?lockpos ?key ?shape) - unlock the place ?lockpos with key ?key of shape ?shape from the current position place ?curpos, (move ?curpos ?nextpos) - move from place ?curpos to place ?nextpos, (pickup ?curpos ?key) - retrieve the key ?key from its current position ?curpos, (pickup-and-loose ?curpos ?newkey ?oldkey) - pick up the key ?newkey from the current position ?curpos and loose the key ?oldkey which is being held, and (putdown ?curpos ?key) - put the key ?key at the current position place ?curpos."
|
| 8 |
+
question: "What action can never become applicable, in any state reachable from the current state?"
|
| 9 |
+
answer: "(unlock f0-3f f0-4f key0-0 shape0)"
|
| 10 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l1-1 and l1-0 are in c1; l0-0 and l0-1 are in c0. Currently, t1 is at l1-1, a0 is at l1-0, p0 is at l0-0, t0 is at l0-1, p2 is in a0, p1 is in t1, p3 is in t0. The available actions are: (load-truck ?obj ?truck ?loc) - load the object ?obj from location ?loc into the truck ?truck, (load-airplane ?obj ?airplane ?loc) - load object ?obj into airplane ?airplane at location ?loc, (unload-truck ?obj ?truck ?loc) - offload the object ?obj from the truck ?truck at location ?loc, (unload-airplane ?obj ?airplane ?loc) - offload the object ?obj from the airplane ?airplane at location ?loc, (drive-truck ?truck ?loc-from ?loc-to ?city) - navigate the truck ?truck from location ?loc-from in city ?city to location ?loc-to in the same city, and (fly-airplane ?airplane ?loc-from ?loc-to) - operate the airplane ?airplane from airport ?loc-from to airport ?loc-to."
|
| 11 |
+
question: "What action can never become applicable, in any state reachable from the current state?"
|
| 12 |
+
answer: "(drive-truck t0 l1-1 l0-0 c0)"
|
| 13 |
+
doc_to_text: "**Question**: {{context}} {{question}} Each action starts with an opening parenthesis and ends with closing parenthesis. Provide one action or None. **Final Answer**:"
|
| 14 |
+
filter_list:
|
| 15 |
+
- name: "acp_grammar_parse"
|
| 16 |
+
filter:
|
| 17 |
+
- function: "ACP_grammar_filter"
|
| 18 |
+
grammar_task: "act"
|
| 19 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/app.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_app_gen
|
| 2 |
+
dataset_name: acp_app_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. The grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. There are 2 keys in 1 different shapes: Key key0-1 is of shape shape0, Key key0-0 is of shape shape0. Currently, the robot is at position f3-2f and its arm is empty. All the positions are open except the following: f2-0f has shape0 shaped lock, f4-2f has shape0 shaped lock. Key key0-0 is at position f2-2f. Key key0-1 is at position f1-3f. The available actions are: (unlock ?curpos ?lockpos ?key ?shape) - unlock the place ?lockpos with key ?key of shape ?shape from the current position place ?curpos, (move ?curpos ?nextpos) - travel from the current position ?curpos to the next position ?nextpos, (pickup ?curpos ?key) - pick up key ?key from place ?curpos, (pickup-and-loose ?curpos ?newkey ?oldkey) - pick up the key ?newkey at the current position place ?curpos and loose the key ?oldkey being held, and (putdown ?curpos ?key) - put down key ?key at current position place ?curpos."
|
| 8 |
+
question: "Generate the list of all ground actions that are applicable in this state."
|
| 9 |
+
answer: "[(move f3-2f f3-1f), (move f3-2f f2-2f), (move f3-2f f3-3f)]"
|
| 10 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l0-0 and l0-1 are in c0; l1-0 and l1-1 are in c1. Currently, t1, p2, and p3 are at l1-0, a0 is at l0-0, t0 is at l0-1, p1 and p0 are in t1. The available actions are: (load-truck ?obj ?truck ?loc) - load object ?obj into truck ?truck at location ?loc, (load-airplane ?obj ?airplane ?loc) - load object ?obj into airplane ?airplane at location ?loc, (unload-truck ?obj ?truck ?loc) - unload object ?obj from truck ?truck at location ?loc, (unload-airplane ?obj ?airplane ?loc) - remove the object ?obj from the airplane ?airplane and place it on the location ?loc, (drive-truck ?truck ?loc-from ?loc-to ?city) - navigate the truck ?truck from its current location ?loc-from in city ?city to the new location ?loc-to within the same city, and (fly-airplane ?airplane ?loc-from ?loc-to) - fly airplane ?airplane from airport ?loc-from to airport ?loc-to."
|
| 11 |
+
question: "Generate the list of all ground actions that are applicable in this state."
|
| 12 |
+
answer: "[(drive-truck t1 l1-0 l1-0 c1), (drive-truck t0 l0-1 l0-0 c0), (load-truck p2 t1 l1-0), (unload-truck p0 t1 l1-0), (drive-truck t0 l0-1 l0-1 c0), (fly-airplane a0 l0-0 l1-0), (fly-airplane a0 l0-0 l0-0), (unload-truck p1 t1 l1-0), (drive-truck t1 l1-0 l1-1 c1), (load-truck p3 t1 l1-0)]"
|
| 13 |
+
doc_to_text: "**Question**: {{context}} {{question}} Each action starts with an opening parenthesis and ends with closing parenthesis. Provide only the actions. **Final Answer**:"
|
| 14 |
+
filter_list:
|
| 15 |
+
- name: "acp_grammar_parse"
|
| 16 |
+
filter:
|
| 17 |
+
- function: "ACP_grammar_filter"
|
| 18 |
+
grammar_task: "action_list"
|
| 19 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/just.yaml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_just_gen
|
| 2 |
+
dataset_name: acp_just_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. The grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. There are 2 keys in 1 different shapes: Key key0-0 is of shape shape0, Key key0-1 is of shape shape0. Currently, the robot is at position f3-3f and its arm is empty. All the positions are open except the following: f4-2f has shape0 shaped lock, f2-0f has shape0 shaped lock. Key key0-0 is at position f2-2f. Key key0-1 is at position f1-3f. The available actions are: (unlock ?curpos ?lockpos ?key ?shape) - unlock place ?lockpos with key ?key of shape ?shape from current position place ?curpos, (move ?curpos ?nextpos) - move from ?curpos to ?nextpos, (pickup ?curpos ?key) - retrieve the key ?key from its current position ?curpos, (pickup-and-loose ?curpos ?newkey ?oldkey) - pick up key ?newkey at current position place ?curpos and loose key ?oldkey being held, and (putdown ?curpos ?key) - put down the key ?key at the current position ?curpos. The goal is to reach a state where the following facts hold: Key key0-0 is at f2-0f location and Key key0-1 is at f1-3f location."
|
| 8 |
+
question: "Simplify the plan [(move f3-3f f3-2f), (move f3-2f f2-2f), (pickup f2-2f key0-0), (move f2-2f f2-1f), (putdown f2-1f key0-0), (pickup f2-1f key0-0), (unlock f2-1f f2-0f key0-0 shape0), (move f2-1f f2-0f), (putdown f2-0f key0-0)] by removing either a single action or a pair of consecutive actions, while still maintaining a valid plan. Provide the resulting simplified plan."
|
| 9 |
+
answer: "[(move f3-3f f3-2f), (move f3-2f f2-2f), (pickup f2-2f key0-0), (move f2-2f f2-1f), (unlock f2-1f f2-0f key0-0 shape0), (move f2-1f f2-0f), (putdown f2-0f key0-0)]"
|
| 10 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l1-1 and l1-0 are in c1; l0-0 and l0-1 are in c0. Currently, p2, p1, and p3 are at l1-0, p0 and t1 are at l1-1, t0 is at l0-1, a0 is at l0-0. The available actions are: (load-truck ?obj ?truck ?loc) - load the object ?obj from location ?loc into the truck ?truck, (load-airplane ?obj ?airplane ?loc) - load the object ?obj from location ?loc onto the airplane ?airplane, (unload-truck ?obj ?truck ?loc) - unload the object ?obj from the truck ?truck at location ?loc, (unload-airplane ?obj ?airplane ?loc) - remove the object ?obj from the airplane ?airplane and place it on the location ?loc, (drive-truck ?truck ?loc-from ?loc-to ?city) - drive truck ?truck from location ?loc-from in city ?city to location ?loc-to in the same city, and (fly-airplane ?airplane ?loc-from ?loc-to) - fly the airplane ?airplane from location ?loc-from to location ?loc-to. The goal is to reach a state where the following facts hold: p3 is at l0-1, p2 is at l1-0, p0 is at l0-0, and p1 is at l1-0."
|
| 11 |
+
question: "Simplify the plan [(fly-airplane a0 l0-0 l1-0), (fly-airplane a0 l1-0 l0-0), (load-truck p0 t1 l1-1), (drive-truck t1 l1-1 l1-0 c1), (unload-truck p0 t1 l1-0), (fly-airplane a0 l0-0 l1-0), (load-airplane p0 a0 l1-0), (load-airplane p3 a0 l1-0), (fly-airplane a0 l1-0 l0-0), (unload-airplane p0 a0 l0-0), (unload-airplane p3 a0 l0-0), (drive-truck t0 l0-1 l0-0 c0), (load-truck p3 t0 l0-0), (drive-truck t0 l0-0 l0-1 c0), (unload-truck p3 t0 l0-1)] by removing either a single action or a pair of consecutive actions, while still maintaining a valid plan. Provide the resulting simplified plan."
|
| 12 |
+
answer: "[(load-truck p0 t1 l1-1), (drive-truck t1 l1-1 l1-0 c1), (unload-truck p0 t1 l1-0), (fly-airplane a0 l0-0 l1-0), (load-airplane p0 a0 l1-0), (load-airplane p3 a0 l1-0), (fly-airplane a0 l1-0 l0-0), (unload-airplane p0 a0 l0-0), (unload-airplane p3 a0 l0-0), (drive-truck t0 l0-1 l0-0 c0), (load-truck p3 t0 l0-0), (drive-truck t0 l0-0 l0-1 c0), (unload-truck p3 t0 l0-1)]"
|
| 13 |
+
doc_to_text: "**Question**: {{context}} {{question}} **Final Answer**:"
|
| 14 |
+
filter_list:
|
| 15 |
+
- name: "acp_grammar_parse"
|
| 16 |
+
filter:
|
| 17 |
+
- function: "ACP_grammar_filter"
|
| 18 |
+
grammar_task: "action_list"
|
| 19 |
+
clean: "simplified plan"
|
| 20 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/land.yaml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_land_gen
|
| 2 |
+
dataset_name: acp_land_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. The grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. There are 2 keys in 1 different shapes: Key key0-1 is of shape shape0, Key key0-0 is of shape shape0. Currently, the robot is at position f3-0f and its arm is empty. All the positions are open except the following: f4-2f has shape0 shaped lock. Key key0-0 is at position f3-0f. Key key0-1 is at position f1-3f. The goal is to reach a state where the following facts hold: Key key0-0 is at f2-0f location and Key key0-1 is at f1-3f location. The available propositions are: (at ?r ?x) - Key ?r is at ?x location, (at-robot ?x) - Robot is at ?x location, (locked ?x) - Location ?x is locked, (holding ?k) - Robot is holding ?k, (open ?x) - Location ?x is open, and (arm-empty) - Robot's arm is empty."
|
| 8 |
+
question: "Generate a non-trivial fact landmark, one that does not hold in the initial state or goal."
|
| 9 |
+
answer: "(holding key0-0)"
|
| 10 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l1-0 and l1-1 are in c1; l0-1 and l0-0 are in c0. Currently, a0 and p2 are at l1-0, t0 is at l0-0, t1 is at l1-1, p3 and p1 are in a0, p0 is in t1. The goal is to reach a state where the following facts hold: p0 is at l0-0, p2 is at l1-0, p1 is at l1-0, and p3 is at l0-1. The available propositions are: (at ?obj ?loc) - ?obj is at ?loc and (in ?obj1 ?obj2) - ?obj1 is in ?obj2."
|
| 11 |
+
question: "Generate a non-trivial fact landmark, one that does not hold in the initial state or goal."
|
| 12 |
+
answer: "(in p3 t0)"
|
| 13 |
+
doc_to_text: "**Question**: {{context}} {{question}} Provide only the ground proposition or None. **Final Answer**:"
|
| 14 |
+
filter_list:
|
| 15 |
+
- name: "acp_grammar_parse"
|
| 16 |
+
filter:
|
| 17 |
+
- function: "ACP_grammar_filter"
|
| 18 |
+
grammar_task: "act"
|
| 19 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/_gen_yaml_2shot
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- acp_gen_2shot_with_pddl
|
| 3 |
+
- acp_bench_hard_with_pddl
|
| 4 |
+
dataset_path: ibm-research/acp_bench
|
| 5 |
+
test_split: test
|
| 6 |
+
description: "Answer the question based on the provided PDDL domain and PDDL problem. The current state is the initial state described in the PDDL problem below.\n\n"
|
| 7 |
+
doc_to_target: "{{answer}}"
|
| 8 |
+
output_type: generate_until
|
| 9 |
+
num_fewshot: 2
|
| 10 |
+
generation_kwargs:
|
| 11 |
+
until:
|
| 12 |
+
- "\n\n\n\n"
|
| 13 |
+
- "\n\n"
|
| 14 |
+
- "**Question**:"
|
| 15 |
+
- "**Question:**"
|
| 16 |
+
- "Q:"
|
| 17 |
+
do_sample: false
|
| 18 |
+
max_gen_toks: 1000
|
| 19 |
+
temperature: 0.0
|
| 20 |
+
metadata:
|
| 21 |
+
version: 1.0
|
| 22 |
+
process_results: !function acp_utils.process_acp_results
|
| 23 |
+
metric_list:
|
| 24 |
+
- metric: "score"
|
| 25 |
+
aggregation: mean
|
| 26 |
+
higher_is_better: True
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/act_reach.yaml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_areach_gen_with_pddl
|
| 2 |
+
dataset_name: acp_areach_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. \nThe grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. \nThere are 2 keys in 0 different shapes: Key key0-1 is of shape shape0, Key key0-0 is of shape shape0. \nCurrently, the robot is at position f3-2f and its arm is empty. All the positions are open except the following: f2-0f has shape0 shaped lock, f4-2f has shape0 shaped lock. Key key0-1 is at position f1-3f. Key key0-0 is at position f2-2f. The available actions are: (unlock ?curpos ?lockpos ?key ?shape) - unlock the place ?lockpos with key ?key of shape ?shape from the current position place ?curpos, (move ?curpos ?nextpos) - move to place ?nextpos from place ?curpos, (pickup ?curpos ?key) - acquire the key ?key from the place ?curpos, (pickup-and-loose ?curpos ?newkey ?oldkey) - pick up key ?newkey at current position place ?curpos and loose key ?oldkey being held, and (putdown ?curpos ?key) - put down key ?key at current position place ?curpos."
|
| 8 |
+
question: "What action can never become applicable, in any state reachable from the current state?"
|
| 9 |
+
answer: "(pickup-and-loose f0-1f key0-0 key0-0)"
|
| 10 |
+
PDDL_domain: "(define (domain grid)\n (:requirements :strips :typing)\n (:types key place shape - object)\n (:predicates (arm-empty) (at ?r - key ?x - place) (at-robot ?x - place) (conn ?x - place ?y - place) (holding ?k - key) (key-shape ?k - key ?s - shape) (lock-shape ?x - place ?s - shape) (locked ?x - place) (open ?x - place))\n (:action move\n :parameters (?curpos - place ?nextpos - place)\n :precondition (and (at-robot ?curpos) (conn ?curpos ?nextpos) (open ?nextpos))\n :effect (and (at-robot ?nextpos) (not (at-robot ?curpos)))\n )\n (:action pickup\n :parameters (?curpos - place ?key - key)\n :precondition (and (at-robot ?curpos) (at ?key ?curpos) (arm-empty))\n :effect (and (holding ?key) (not (at ?key ?curpos)) (not (arm-empty)))\n )\n (:action pickup-and-loose\n :parameters (?curpos - place ?newkey - key ?oldkey - key)\n :precondition (and (at-robot ?curpos) (holding ?oldkey) (at ?newkey ?curpos))\n :effect (and (holding ?newkey) (at ?oldkey ?curpos) (not (holding ?oldkey)) (not (at ?newkey ?curpos)))\n )\n (:action putdown\n :parameters (?curpos - place ?key - key)\n :precondition (and (at-robot ?curpos) (holding ?key))\n :effect (and (arm-empty) (at ?key ?curpos) (not (holding ?key)))\n )\n (:action unlock\n :parameters (?curpos - place ?lockpos - place ?key - key ?shape - shape)\n :precondition (and (conn ?curpos ?lockpos) (key-shape ?key ?shape) (lock-shape ?lockpos ?shape) (at-robot ?curpos) (locked ?lockpos) (holding ?key))\n :effect (and (open ?lockpos) (not (locked ?lockpos)))\n )\n)"
|
| 11 |
+
PDDL_problem: "(define (problem grid-x5-y5-t1-k2-l2-p100)\n (:domain grid)\n (:requirements :strips :typing)\n (:objects key0-0 key0-1 - key f0-0f f0-1f f0-2f f0-3f f0-4f f1-0f f1-1f f1-2f f1-3f f1-4f f2-0f f2-1f f2-2f f2-3f f2-4f f3-0f f3-1f f3-2f f3-3f f3-4f f4-0f f4-1f f4-2f f4-3f f4-4f - place shape0 - shape)\n (:init (arm-empty) (at key0-0 f2-2f) (at key0-1 f1-3f) (at-robot f3-2f) (conn f0-0f f0-1f) (conn f0-0f f1-0f) (conn f0-1f f0-0f) (conn f0-1f f0-2f) (conn f0-1f f1-1f) (conn f0-2f f0-1f) (conn f0-2f f0-3f) (conn f0-2f f1-2f) (conn f0-3f f0-2f) (conn f0-3f f0-4f) (conn f0-3f f1-3f) (conn f0-4f f0-3f) (conn f0-4f f1-4f) (conn f1-0f f0-0f) (conn f1-0f f1-1f) (conn f1-0f f2-0f) (conn f1-1f f0-1f) (conn f1-1f f1-0f) (conn f1-1f f1-2f) (conn f1-1f f2-1f) (conn f1-2f f0-2f) (conn f1-2f f1-1f) (conn f1-2f f1-3f) (conn f1-2f f2-2f) (conn f1-3f f0-3f) (conn f1-3f f1-2f) (conn f1-3f f1-4f) (conn f1-3f f2-3f) (conn f1-4f f0-4f) (conn f1-4f f1-3f) (conn f1-4f f2-4f) (conn f2-0f f1-0f) (conn f2-0f f2-1f) (conn f2-0f f3-0f) (conn f2-1f f1-1f) (conn f2-1f f2-0f) (conn f2-1f f2-2f) (conn f2-1f f3-1f) (conn f2-2f f1-2f) (conn f2-2f f2-1f) (conn f2-2f f2-3f) (conn f2-2f f3-2f) (conn f2-3f f1-3f) (conn f2-3f f2-2f) (conn f2-3f f2-4f) (conn f2-3f f3-3f) (conn f2-4f f1-4f) (conn f2-4f f2-3f) (conn f2-4f f3-4f) (conn f3-0f f2-0f) (conn f3-0f f3-1f) (conn f3-0f f4-0f) (conn f3-1f f2-1f) (conn f3-1f f3-0f) (conn f3-1f f3-2f) (conn f3-1f f4-1f) (conn f3-2f f2-2f) (conn f3-2f f3-1f) (conn f3-2f f3-3f) (conn f3-2f f4-2f) (conn f3-3f f2-3f) (conn f3-3f f3-2f) (conn f3-3f f3-4f) (conn f3-3f f4-3f) (conn f3-4f f2-4f) (conn f3-4f f3-3f) (conn f3-4f f4-4f) (conn f4-0f f3-0f) (conn f4-0f f4-1f) (conn f4-1f f3-1f) (conn f4-1f f4-0f) (conn f4-1f f4-2f) (conn f4-2f f3-2f) (conn f4-2f f4-1f) (conn f4-2f f4-3f) (conn f4-3f f3-3f) (conn f4-3f f4-2f) (conn f4-3f f4-4f) (conn f4-4f f3-4f) (conn f4-4f f4-3f) (key-shape key0-0 shape0) (key-shape key0-1 shape0) (lock-shape f2-0f shape0) (lock-shape f4-2f shape0) (locked f2-0f) (locked f4-2f) (open f0-0f) (open f0-1f) (open f0-2f) (open f0-3f) (open f0-4f) (open f1-0f) (open f1-1f) (open f1-2f) (open f1-3f) (open f1-4f) (open f2-1f) (open f2-2f) (open f2-3f) (open f2-4f) (open f3-0f) (open f3-1f) (open f3-2f) (open f3-3f) (open f3-4f) (open f4-0f) (open f4-1f) (open f4-3f) (open f4-4f))\n (:goal (and (at key0-0 f2-0f) (at key0-1 f1-3f)))\n)"
|
| 12 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. \nThere are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. \nThe locations are in cities as follows: l1-0 and l1-1 are in c1; l0-0 and l0-1 are in c0. \nCurrently, a0, p1, and p2 are at l1-0, t0 is at l0-1, p3 and p0 are at l0-0, t1 is at l1-1. The available actions are: (load-truck ?obj ?truck ?loc) - place the object ?obj into the truck ?truck at location ?loc, (load-airplane ?obj ?airplane ?loc) - load the object ?obj from location ?loc into the airplane ?airplane, (unload-truck ?obj ?truck ?loc) - unload object ?obj from truck ?truck at location ?loc, (unload-airplane ?obj ?airplane ?loc) - unload object ?obj from airplane ?airplane at location ?loc, (drive-truck ?truck ?loc-from ?loc-to ?city) - navigate the truck ?truck from its current location ?loc-from in city ?city to the new location ?loc-to within the same city, and (fly-airplane ?airplane ?loc-from ?loc-to) - fly airplane ?airplane from airport ?loc-from to airport ?loc-to."
|
| 13 |
+
question: "What action can never become applicable, in any state reachable from the current state??"
|
| 14 |
+
answer: "(load-truck p2 t0 l1-1)"
|
| 15 |
+
PDDL_domain: "(define (domain logistics-strips)\n (:requirements :strips :typing) \n\n (:types \n location locatable city - object \n package movable - locatable\n airport - location\n airplane truck - movable \n )\t\t\n \n (:predicates \t\n\t\t(at ?obj - locatable ?loc - location)\n\t\t(in ?obj1 - package ?obj2 - movable)\n\t\t(in-city ?obj - location ?city - city))\n\n\n(:action LOAD-TRUCK\n :parameters\n (?obj - package\n ?truck - truck\n ?loc - location)\n :precondition\n (and \n (at ?truck ?loc) (at ?obj ?loc))\n :effect\n (and (not (at ?obj ?loc)) (in ?obj ?truck)))\n\n(:action LOAD-AIRPLANE\n :parameters\n (?obj - package\n ?airplane - airplane\n ?loc - location)\n :precondition\n (and \n (at ?obj ?loc) (at ?airplane ?loc))\n :effect\n (and (not (at ?obj ?loc)) (in ?obj ?airplane)))\n\n\n\n(:action UNLOAD-TRUCK\n :parameters\n (?obj - package\n ?truck - truck\n ?loc - location)\n :precondition\n (and \n (at ?truck ?loc) (in ?obj ?truck))\n :effect\n (and (not (in ?obj ?truck)) (at ?obj ?loc)))\n\n(:action UNLOAD-AIRPLANE\n :parameters\n (?obj - package\n ?airplane - airplane\n ?loc - location)\n :precondition\n (and \n (in ?obj ?airplane) (at ?airplane ?loc))\n :effect\n (and (not (in ?obj ?airplane)) (at ?obj ?loc)))\n\n(:action DRIVE-TRUCK\n :parameters\n (?truck - truck\n ?loc-from - location\n ?loc-to - location\n ?city - city)\n :precondition\n (and \n (at ?truck ?loc-from)\n (in-city ?loc-from ?city)\n (in-city ?loc-to ?city))\n :effect\n (and (not (at ?truck ?loc-from)) (at ?truck ?loc-to)))\n\n(:action FLY-AIRPLANE\n :parameters\n (?airplane - airplane\n ?loc-from - airport\n ?loc-to - airport)\n :precondition\n (and \n\t(at ?airplane ?loc-from))\n :effect\n (and (not (at ?airplane ?loc-from)) (at ?airplane ?loc-to)))\n)"
|
| 16 |
+
PDDL_problem: "(define (problem logistics-c2-s2-p4-a1)\n (:domain logistics-strips)\n (:requirements :strips :typing)\n (:objects a0 - airplane l0-0 l1-0 - airport c0 c1 - city l0-1 l1-1 - location p0 p1 p2 p3 - package t0 t1 - truck)\n (:init (at a0 l1-0) (at p0 l0-0) (at p1 l1-0) (at p2 l1-0) (at p3 l0-0) (at t0 l0-1) (at t1 l1-1) (in-city l0-0 c0) (in-city l0-1 c0) (in-city l1-0 c1) (in-city l1-1 c1))\n (:goal (and (at p0 l0-0) (at p1 l1-0) (at p2 l1-0) (at p3 l0-1)))\n)"
|
| 17 |
+
doc_to_text: "# PDDL DOMAIN \n\n```\n{{PDDL_domain}}\n```\n\n# PDDL PROBLEM \n\n```\n{{PDDL_problem}}\n```\n\n**Question**: {{context}} {{question}} Each action starts with an opening parenthesis and ends with closing parenthesis. Provide one action or None. **Final Answer**:"
|
| 18 |
+
filter_list:
|
| 19 |
+
- name: "acp_grammar_parse"
|
| 20 |
+
filter:
|
| 21 |
+
- function: "ACP_grammar_filter"
|
| 22 |
+
grammar_task: "act"
|
| 23 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/land.yaml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_land_gen_with_pddl
|
| 2 |
+
dataset_name: acp_land_gen
|
| 3 |
+
include: _gen_yaml_2shot
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: "A robot is in a grid and can only move to places that are connected to its current position. \nThe grid size is 5x5, and the locations are of the form fi-jf (e.g., f3-2f or f0-1f). The grid cells are connected to their neighbors (e.g., f1-2f is connected to the four neighbors f0-2f, f2-2f, f1-1f, and f1-3f). Some positions on the grid are locked and can be opened with a key of a matching shape. The robot has an arm that can pick up a key when the key is in same location as the robot and the arm is empty. \nThere are 2 keys in 0 different shapes: Key key0-0 is of shape shape0, Key key0-1 is of shape shape0. \nCurrently, the robot is at position f1-0f and its arm is empty. All the positions are open except the following: f4-2f has shape0 shaped lock. Key key0-1 is at position f1-3f. Key key0-0 is at position f1-0f. The goal is to reach a state where the following facts hold: Key key0-1 is at f1-3f location and Key key0-0 is at f2-0f location. The available propositions are: (at ?r ?x) - Key ?r is at ?x location, (at-robot ?x) - Robot is at ?x location, (locked ?x) - Location ?x is locked, (holding ?k) - Robot is holding ?k, (open ?x) - Location ?x is open, and (arm-empty) - Robot's arm is empty."
|
| 8 |
+
question: "Generate a non-trivial fact landmark, one that does not hold in the initial state or goal."
|
| 9 |
+
answer: "(at-robot f2-0f)"
|
| 10 |
+
PDDL_domain: "(define (domain grid)\n (:requirements :strips :typing)\n (:types key place shape - object)\n (:predicates (arm-empty) (at ?r - key ?x - place) (at-robot ?x - place) (conn ?x - place ?y - place) (holding ?k - key) (key-shape ?k - key ?s - shape) (lock-shape ?x - place ?s - shape) (locked ?x - place) (open ?x - place))\n (:action move\n :parameters (?curpos - place ?nextpos - place)\n :precondition (and (at-robot ?curpos) (conn ?curpos ?nextpos) (open ?nextpos))\n :effect (and (at-robot ?nextpos) (not (at-robot ?curpos)))\n )\n (:action pickup\n :parameters (?curpos - place ?key - key)\n :precondition (and (at-robot ?curpos) (at ?key ?curpos) (arm-empty))\n :effect (and (holding ?key) (not (at ?key ?curpos)) (not (arm-empty)))\n )\n (:action pickup-and-loose\n :parameters (?curpos - place ?newkey - key ?oldkey - key)\n :precondition (and (at-robot ?curpos) (holding ?oldkey) (at ?newkey ?curpos))\n :effect (and (holding ?newkey) (at ?oldkey ?curpos) (not (holding ?oldkey)) (not (at ?newkey ?curpos)))\n )\n (:action putdown\n :parameters (?curpos - place ?key - key)\n :precondition (and (at-robot ?curpos) (holding ?key))\n :effect (and (arm-empty) (at ?key ?curpos) (not (holding ?key)))\n )\n (:action unlock\n :parameters (?curpos - place ?lockpos - place ?key - key ?shape - shape)\n :precondition (and (conn ?curpos ?lockpos) (key-shape ?key ?shape) (lock-shape ?lockpos ?shape) (at-robot ?curpos) (locked ?lockpos) (holding ?key))\n :effect (and (open ?lockpos) (not (locked ?lockpos)))\n )\n)"
|
| 11 |
+
PDDL_problem: "(define (problem grid-x5-y5-t1-k2-l2-p100)\n (:domain grid)\n (:requirements :strips :typing)\n (:objects key0-0 key0-1 - key f0-0f f0-1f f0-2f f0-3f f0-4f f1-0f f1-1f f1-2f f1-3f f1-4f f2-0f f2-1f f2-2f f2-3f f2-4f f3-0f f3-1f f3-2f f3-3f f3-4f f4-0f f4-1f f4-2f f4-3f f4-4f - place shape0 - shape)\n (:init (arm-empty) (at key0-0 f1-0f) (at key0-1 f1-3f) (at-robot f1-0f) (conn f0-0f f0-1f) (conn f0-0f f1-0f) (conn f0-1f f0-0f) (conn f0-1f f0-2f) (conn f0-1f f1-1f) (conn f0-2f f0-1f) (conn f0-2f f0-3f) (conn f0-2f f1-2f) (conn f0-3f f0-2f) (conn f0-3f f0-4f) (conn f0-3f f1-3f) (conn f0-4f f0-3f) (conn f0-4f f1-4f) (conn f1-0f f0-0f) (conn f1-0f f1-1f) (conn f1-0f f2-0f) (conn f1-1f f0-1f) (conn f1-1f f1-0f) (conn f1-1f f1-2f) (conn f1-1f f2-1f) (conn f1-2f f0-2f) (conn f1-2f f1-1f) (conn f1-2f f1-3f) (conn f1-2f f2-2f) (conn f1-3f f0-3f) (conn f1-3f f1-2f) (conn f1-3f f1-4f) (conn f1-3f f2-3f) (conn f1-4f f0-4f) (conn f1-4f f1-3f) (conn f1-4f f2-4f) (conn f2-0f f1-0f) (conn f2-0f f2-1f) (conn f2-0f f3-0f) (conn f2-1f f1-1f) (conn f2-1f f2-0f) (conn f2-1f f2-2f) (conn f2-1f f3-1f) (conn f2-2f f1-2f) (conn f2-2f f2-1f) (conn f2-2f f2-3f) (conn f2-2f f3-2f) (conn f2-3f f1-3f) (conn f2-3f f2-2f) (conn f2-3f f2-4f) (conn f2-3f f3-3f) (conn f2-4f f1-4f) (conn f2-4f f2-3f) (conn f2-4f f3-4f) (conn f3-0f f2-0f) (conn f3-0f f3-1f) (conn f3-0f f4-0f) (conn f3-1f f2-1f) (conn f3-1f f3-0f) (conn f3-1f f3-2f) (conn f3-1f f4-1f) (conn f3-2f f2-2f) (conn f3-2f f3-1f) (conn f3-2f f3-3f) (conn f3-2f f4-2f) (conn f3-3f f2-3f) (conn f3-3f f3-2f) (conn f3-3f f3-4f) (conn f3-3f f4-3f) (conn f3-4f f2-4f) (conn f3-4f f3-3f) (conn f3-4f f4-4f) (conn f4-0f f3-0f) (conn f4-0f f4-1f) (conn f4-1f f3-1f) (conn f4-1f f4-0f) (conn f4-1f f4-2f) (conn f4-2f f3-2f) (conn f4-2f f4-1f) (conn f4-2f f4-3f) (conn f4-3f f3-3f) (conn f4-3f f4-2f) (conn f4-3f f4-4f) (conn f4-4f f3-4f) (conn f4-4f f4-3f) (key-shape key0-0 shape0) (key-shape key0-1 shape0) (lock-shape f2-0f shape0) (lock-shape f4-2f shape0) (locked f4-2f) (open f0-0f) (open f0-1f) (open f0-2f) (open f0-3f) (open f0-4f) (open f1-0f) (open f1-1f) (open f1-2f) (open f1-3f) (open f1-4f) (open f2-0f) (open f2-1f) (open f2-2f) (open f2-3f) (open f2-4f) (open f3-0f) (open f3-1f) (open f3-2f) (open f3-3f) (open f3-4f) (open f4-0f) (open f4-1f) (open f4-3f) (open f4-4f))\n (:goal (and (at key0-0 f2-0f) (at key0-1 f1-3f)))\n)"
|
| 12 |
+
- context: "There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. \nThere are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. \nThe locations are in cities as follows: l0-1 and l0-0 are in c0; l1-0 and l1-1 are in c1. \nCurrently, a0 and t0 are at l0-0, t1 is at l1-1, p2, p1, and p3 are at l1-0, p0 is in t1. The goal is to reach a state where the following facts hold: p1 is at l1-0, p0 is at l0-0, p2 is at l1-0, and p3 is at l0-1. The available propositions are: (at ?obj ?loc) - ?obj is at ?loc and (in ?obj1 ?obj2) - ?obj1 is in ?obj2."
|
| 13 |
+
question: "Generate a non-trivial fact landmark, one that does not hold in the initial state or goal."
|
| 14 |
+
answer: "(in p3 t0)"
|
| 15 |
+
PDDL_domain: "(define (domain logistics-strips)\n (:requirements :strips :typing) \n\n (:types \n location locatable city - object \n package movable - locatable\n airport - location\n airplane truck - movable \n )\t\t\n \n (:predicates \t\n\t\t(at ?obj - locatable ?loc - location)\n\t\t(in ?obj1 - package ?obj2 - movable)\n\t\t(in-city ?obj - location ?city - city))\n\n\n(:action LOAD-TRUCK\n :parameters\n (?obj - package\n ?truck - truck\n ?loc - location)\n :precondition\n (and \n (at ?truck ?loc) (at ?obj ?loc))\n :effect\n (and (not (at ?obj ?loc)) (in ?obj ?truck)))\n\n(:action LOAD-AIRPLANE\n :parameters\n (?obj - package\n ?airplane - airplane\n ?loc - location)\n :precondition\n (and \n (at ?obj ?loc) (at ?airplane ?loc))\n :effect\n (and (not (at ?obj ?loc)) (in ?obj ?airplane)))\n\n\n\n(:action UNLOAD-TRUCK\n :parameters\n (?obj - package\n ?truck - truck\n ?loc - location)\n :precondition\n (and \n (at ?truck ?loc) (in ?obj ?truck))\n :effect\n (and (not (in ?obj ?truck)) (at ?obj ?loc)))\n\n(:action UNLOAD-AIRPLANE\n :parameters\n (?obj - package\n ?airplane - airplane\n ?loc - location)\n :precondition\n (and \n (in ?obj ?airplane) (at ?airplane ?loc))\n :effect\n (and (not (in ?obj ?airplane)) (at ?obj ?loc)))\n\n(:action DRIVE-TRUCK\n :parameters\n (?truck - truck\n ?loc-from - location\n ?loc-to - location\n ?city - city)\n :precondition\n (and \n (at ?truck ?loc-from)\n (in-city ?loc-from ?city)\n (in-city ?loc-to ?city))\n :effect\n (and (not (at ?truck ?loc-from)) (at ?truck ?loc-to)))\n\n(:action FLY-AIRPLANE\n :parameters\n (?airplane - airplane\n ?loc-from - airport\n ?loc-to - airport)\n :precondition\n (and \n\t(at ?airplane ?loc-from))\n :effect\n (and (not (at ?airplane ?loc-from)) (at ?airplane ?loc-to)))\n)"
|
| 16 |
+
PDDL_problem: "(define (problem logistics-c2-s2-p4-a1)\n (:domain logistics-strips)\n (:requirements :strips :typing)\n (:objects a0 - airplane l0-0 l1-0 - airport c0 c1 - city l0-1 l1-1 - location p0 p1 p2 p3 - package t0 t1 - truck)\n (:init (at a0 l0-0) (at p1 l1-0) (at p2 l1-0) (at p3 l1-0) (at t0 l0-0) (at t1 l1-1) (in p0 t1) (in-city l0-0 c0) (in-city l0-1 c0) (in-city l1-0 c1) (in-city l1-1 c1))\n (:goal (and (at p0 l0-0) (at p1 l1-0) (at p2 l1-0) (at p3 l0-1)))\n)"
|
| 17 |
+
doc_to_text: "# PDDL DOMAIN \n\n```\n{{PDDL_domain}}\n```\n\n# PDDL PROBLEM \n\n```\n{{PDDL_problem}}\n```\n\n**Question**: {{context}} {{question}} Provide only the ground proposition or None. **Final Answer**:"
|
| 18 |
+
filter_list:
|
| 19 |
+
- name: "acp_grammar_parse"
|
| 20 |
+
filter:
|
| 21 |
+
- function: "ACP_grammar_filter"
|
| 22 |
+
grammar_task: "act"
|
| 23 |
+
- function: "take_first"
|
lm-evaluation-harness/lm_eval/tasks/acpbench/mcq_cot_2shot/just.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
task: acp_just_mcq
|
| 2 |
+
dataset_name: acp_just_mcq
|
| 3 |
+
include: _mcq_cot_2shot_yaml
|
| 4 |
+
fewshot_config:
|
| 5 |
+
sampler: first_n
|
| 6 |
+
samples:
|
| 7 |
+
- context: 'This is a ferry domain, where the task is to transport cars from their start to their goal locations, using a ferry. Each location is accessible by ferry from each other location. The cars can be debarked or boarded, and the ferry can carry only one car at a time. There are 2 locations and 2 cars, numbered consecutively. Currently, the ferry is at l0 location and it is empty. The cars are at locations as follows: c0 and c1 are at l0. The goal is to reach a state where the following facts hold: Car c1 is at location l1 and Car c0 is at location l1.'
|
| 8 |
+
question: 'Given the plan: \"board the car c0 at the location l0, travel by sea from location l0 to location l1, unload the car c0 from the ferry to location l1, travel by sea from location l1 to location l0, board the car c1 at location l0, sail from location l0 to location l1, debark the car c1 from the ferry to location l1, travel by sea from location l1 to location l0\"; which of the following actions can be removed from this plan and still have a valid plan? A. board the car c1 at the location l0. B. debark car c1 to location l1 from the ferry. C. travel by sea from location l1 to location l0. D. sail from location l0 to location l1.'
|
| 9 |
+
answer: "Let's think step by step. Step 1: A valid plan is a sequence of actions that is exectuable from the current state and achieves the goal. Step 2: The 2nd instance of \"travel by sea from location l1 to location l0\" can be removed from the plan and still have a valid plan. **Final Answer**: C."
|
| 10 |
+
- context: 'There are several cities, each containing several locations, some of which are airports. There are also trucks, which can drive within a single city, and airplanes, which can fly between airports. The goal is to get some packages from various locations to various new locations. There are 2 trucks and 1 airplane, as well as 4 packages. There are 4 locations across 2 cities. The locations are in cities as follows: l0-0 and l0-1 are in c0; l1-0 and l1-1 are in c1. Currently, p2, p1, and p3 are at l1-0, p0 and t1 are at l1-1, a0 is at l0-0, t0 is at l0-1. The goal is to reach a state where the following facts hold: p3 is at l0-1, p0 is at l0-0, p2 is at l1-0, and p1 is at l1-0.'
|
| 11 |
+
question: 'Given the plan: \"load object p0 into truck t1 at location l1-1, navigate the truck t1 which is in location l1-1 in city c1 to another location l1-0 in the same city, offload the object p0 from the truck t1 at location l1-0, fly the airplane a0 from location l0-0 to location l1-0, load object p0 into airplane a0 at location l1-0, place the object p3 onto the airplane a0 at location l1-0, fly the airplane a0 from location l1-0 to location l0-0, unload the object p0 from the airplane a0 at location l0-0, unload the object p3 from the airplane a0 at location l0-0, navigate the truck t0 from location l0-1 in city c0 to location l0-0 in the same city, load object p3 into truck t0 at location l0-0, drive truck t0 from location l0-0 in city c0 to location l0-1 in the same city, offload the object p3 from the truck t0 at location l0-1, drive truck t0 from location l0-1 in city c0 to location l0-0 in the same city, navigate the truck t0 from its current location l0-0 in city c0 to the new location l0-1 within the same city\"; which of the following pair of consecutive actions can be removed from this plan and still have a valid plan? A. drive the truck t0 in city c0 from location l0-1 to location l0-0 and navigate the truck t0 which is in location l0-0 in city c0 to another location l0-1 in the same city. B. unload object p3 from truck t0 at location l0-1 and drive truck t0 from location l0-1 in city c0 to location l0-0 in the same city. C. drive truck t0 from location l0-1 in city c0 to location l0-0 in the same city and load the object p3 from location l0-0 into the truck t0. D. unload the object p0 from the airplane a0 at location l0-0 and remove the object p3 from the airplane a0 and place it on the location l0-0.'
|
| 12 |
+
answer: "Let's think step by step. Step 1: A valid plan is a sequence of actions that is exectuable from the current state and achieves the goal. Step 2: Consecutive actions \"drive the truck t0 in city c0 from location l0-1 to location l0-0 and navigate the truck t0 which is in location l0-0 in city c0 to another location l0-1 in the same city\" can be removed from the plan and still have a valid plan. **Final Answer**: A."
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- afrimgsm_tasks
|
| 3 |
+
- afrimgsm_tasks_prompt_4
|
| 4 |
+
dataset_path: masakhane/afrimgsm
|
| 5 |
+
output_type: generate_until
|
| 6 |
+
test_split: test
|
| 7 |
+
doc_to_target: '{% if answer is not none %}{{answer[21:]}}{% else %}{{answer_number|string}}{% endif %}'
|
| 8 |
+
target_delimiter: ""
|
| 9 |
+
generation_kwargs:
|
| 10 |
+
do_sample: false
|
| 11 |
+
until:
|
| 12 |
+
- 'Question:'
|
| 13 |
+
- </s>
|
| 14 |
+
- <|im_end|>
|
| 15 |
+
filter_list:
|
| 16 |
+
- name: remove_whitespace
|
| 17 |
+
filter:
|
| 18 |
+
- function: remove_whitespace
|
| 19 |
+
- function: take_first
|
| 20 |
+
- filter:
|
| 21 |
+
- function: regex
|
| 22 |
+
group_select: -1
|
| 23 |
+
regex_pattern: (-?[$0-9.,]{2,})|(-?[0-9]+)
|
| 24 |
+
- function: take_first
|
| 25 |
+
name: flexible-extract
|
| 26 |
+
metric_list:
|
| 27 |
+
- metric: exact_match
|
| 28 |
+
aggregation: mean
|
| 29 |
+
higher_is_better: true
|
| 30 |
+
ignore_case: true
|
| 31 |
+
ignore_punctuation: true
|
| 32 |
+
metadata:
|
| 33 |
+
version: 2.0
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_zul.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: zul
|
| 3 |
+
doc_to_text: "Answer the given question with the appropriate numerical value, ensuring\
|
| 4 |
+
\ that the response is clear and without any supplementary information. \n\nQuestion:\
|
| 5 |
+
\ {{question}} \nAnswer: "
|
| 6 |
+
include: afrimgsm_yaml
|
| 7 |
+
task: afrimgsm_zul_prompt_4
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sna.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: sna
|
| 3 |
+
doc_to_text: "For mathematical questions provided in chiShona language. Supply the\
|
| 4 |
+
\ accurate numeric answer to the provided question. \n\nQuestion: {{question}} \n\
|
| 5 |
+
Answer: "
|
| 6 |
+
include: afrimgsm_yaml
|
| 7 |
+
task: afrimgsm_sna_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sot.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: sot
|
| 3 |
+
doc_to_text: "For mathematical questions provided in Sesotho language. Supply the\
|
| 4 |
+
\ accurate numeric answer to the provided question. \n\nQuestion: {{question}} \n\
|
| 5 |
+
Answer: "
|
| 6 |
+
include: afrimgsm_yaml
|
| 7 |
+
task: afrimgsm_sot_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_twi.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: twi
|
| 3 |
+
doc_to_text: "For mathematical questions provided in Twi language. Supply the accurate\
|
| 4 |
+
\ numeric answer to the provided question. \n\nQuestion: {{question}} \nAnswer: "
|
| 5 |
+
include: afrimgsm_yaml
|
| 6 |
+
task: afrimgsm_twi_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_vai.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: vai
|
| 3 |
+
doc_to_text: "For mathematical questions provided in Vai language. Supply the accurate\
|
| 4 |
+
\ numeric answer to the provided question. \n\nQuestion: {{question}} \nAnswer: "
|
| 5 |
+
include: afrimgsm_yaml
|
| 6 |
+
task: afrimgsm_vai_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yaml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- afrimgsm_tasks
|
| 3 |
+
- afrimgsm_tasks_prompt_5
|
| 4 |
+
dataset_path: masakhane/afrimgsm
|
| 5 |
+
output_type: generate_until
|
| 6 |
+
test_split: test
|
| 7 |
+
doc_to_target: '{% if answer is not none %}{{answer[21:]}}{% else %}{{answer_number|string}}{% endif %}'
|
| 8 |
+
target_delimiter: ""
|
| 9 |
+
generation_kwargs:
|
| 10 |
+
do_sample: false
|
| 11 |
+
until:
|
| 12 |
+
- 'Question:'
|
| 13 |
+
- </s>
|
| 14 |
+
- <|im_end|>
|
| 15 |
+
filter_list:
|
| 16 |
+
- name: remove_whitespace
|
| 17 |
+
filter:
|
| 18 |
+
- function: remove_whitespace
|
| 19 |
+
- function: take_first
|
| 20 |
+
- filter:
|
| 21 |
+
- function: regex
|
| 22 |
+
group_select: -1
|
| 23 |
+
regex_pattern: (-?[$0-9.,]{2,})|(-?[0-9]+)
|
| 24 |
+
- function: take_first
|
| 25 |
+
name: flexible-extract
|
| 26 |
+
metric_list:
|
| 27 |
+
- metric: exact_match
|
| 28 |
+
aggregation: mean
|
| 29 |
+
higher_is_better: true
|
| 30 |
+
ignore_case: true
|
| 31 |
+
ignore_punctuation: true
|
| 32 |
+
metadata:
|
| 33 |
+
version: 2.0
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yor.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: yor
|
| 3 |
+
doc_to_text: "For mathematical questions provided in Yoruba language. Supply the accurate\
|
| 4 |
+
\ numeric answer to the provided question. \n\nQuestion: {{question}} \nAnswer: "
|
| 5 |
+
include: afrimgsm_yaml
|
| 6 |
+
task: afrimgsm_yor_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_zul.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: zul
|
| 3 |
+
doc_to_text: "For mathematical questions provided in Zulu language. Supply the accurate\
|
| 4 |
+
\ numeric answer to the provided question. \n\nQuestion: {{question}} \nAnswer: "
|
| 5 |
+
include: afrimgsm_yaml
|
| 6 |
+
task: afrimgsm_zul_prompt_5
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lin.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: lin
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_lin_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lug.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: lug
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_lug_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_swa.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: swa
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_swa_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_twi.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: twi
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_twi_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_vai.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: vai
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_vai_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_wol.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: wol
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_wol_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_xho.yaml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by utils.py
|
| 2 |
+
dataset_name: xho
|
| 3 |
+
include: afrimgsm_cot_yaml
|
| 4 |
+
task: afrimgsm_cot_xho_prompt_1
|
lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_yaml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tag:
|
| 2 |
+
- afrimgsm_cot_tasks
|
| 3 |
+
- afrimgsm_cot_tasks_prompt_1
|
| 4 |
+
dataset_path: masakhane/afrimgsm
|
| 5 |
+
dataset_name: null # Overridden by language-specific config.
|
| 6 |
+
output_type: generate_until
|
| 7 |
+
training_split: train
|
| 8 |
+
test_split: test
|
| 9 |
+
doc_to_target: '{% if answer is not none %}{{answer[21:]}}{% else %}{{answer_number|string}}{% endif %}'
|
| 10 |
+
doc_to_text: '{% if answer is not none %}{{question+"\nStep-by-Step Answer:"}}{% else %}{{"Question: "+question+"\nStep-by-Step Answer:"}}{% endif %}'
|
| 11 |
+
generation_kwargs:
|
| 12 |
+
do_sample: false
|
| 13 |
+
until:
|
| 14 |
+
- 'Question:'
|
| 15 |
+
- </s>
|
| 16 |
+
- <|im_end|>
|
| 17 |
+
- <|eot_id|>
|
| 18 |
+
metric_list:
|
| 19 |
+
- metric: exact_match
|
| 20 |
+
aggregation: mean
|
| 21 |
+
higher_is_better: true
|
| 22 |
+
ignore_case: true
|
| 23 |
+
ignore_punctuation: true
|
| 24 |
+
filter_list:
|
| 25 |
+
- name: "strict-match"
|
| 26 |
+
filter:
|
| 27 |
+
- function: "regex"
|
| 28 |
+
regex_pattern: "The answer is (\\-?[0-9\\.\\,]+)"
|
| 29 |
+
- function: "take_first"
|
| 30 |
+
- filter:
|
| 31 |
+
- function: regex
|
| 32 |
+
group_select: -1
|
| 33 |
+
regex_pattern: (-?[$0-9.,]{2,})|(-?[0-9]+)
|
| 34 |
+
- function: take_first
|
| 35 |
+
name: flexible-extract
|
| 36 |
+
metadata:
|
| 37 |
+
version: 2.0
|