chen459664 commited on
Commit
8d423ba
·
verified ·
1 Parent(s): 8409f53

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lm-evaluation-harness/lm_eval/models/__pycache__/gguf.cpython-310.pyc +0 -0
  2. lm-evaluation-harness/lm_eval/models/__pycache__/hf_steered.cpython-311.pyc +0 -0
  3. lm-evaluation-harness/lm_eval/models/__pycache__/ibm_watsonx_ai.cpython-311.pyc +0 -0
  4. lm-evaluation-harness/lm_eval/models/__pycache__/mamba_lm.cpython-310.pyc +0 -0
  5. lm-evaluation-harness/lm_eval/models/__pycache__/nemo_lm.cpython-311.pyc +0 -0
  6. lm-evaluation-harness/lm_eval/models/__pycache__/openai_completions.cpython-310.pyc +0 -0
  7. lm-evaluation-harness/lm_eval/models/__pycache__/sglang_causallms.cpython-311.pyc +0 -0
  8. lm-evaluation-harness/lm_eval/models/hf_steered.py +243 -0
  9. lm-evaluation-harness/lm_eval/models/openai_completions.py +296 -0
  10. lm-evaluation-harness/lm_eval/models/utils.py +854 -0
  11. lm-evaluation-harness/lm_eval/models/vllm_vlms.py +323 -0
  12. lm-evaluation-harness/lm_eval/tasks/aclue/README.md +50 -0
  13. lm-evaluation-harness/lm_eval/tasks/aclue/aclue_named_entity_recognition.yaml +4 -0
  14. lm-evaluation-harness/lm_eval/tasks/aclue/aclue_poetry_context_prediction.yaml +4 -0
  15. lm-evaluation-harness/lm_eval/tasks/aclue/aclue_reading_comprehension.yaml +4 -0
  16. lm-evaluation-harness/lm_eval/tasks/aclue/aclue_sentence_segmentation.yaml +4 -0
  17. lm-evaluation-harness/lm_eval/tasks/acpbench/README.md +130 -0
  18. lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/_boolq_cot_2shot_yaml +32 -0
  19. lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/app.yaml +12 -0
  20. lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/just.yaml +13 -0
  21. lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/land.yaml +13 -0
  22. lm-evaluation-harness/lm_eval/tasks/acpbench/boolq_cot_2shot/val.yaml +12 -0
  23. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/_gen_yaml_2shot +25 -0
  24. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_grammar.lark +23 -0
  25. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/acp_utils.py +1128 -0
  26. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/act_reach.yaml +19 -0
  27. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/app.yaml +19 -0
  28. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/just.yaml +20 -0
  29. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot/land.yaml +19 -0
  30. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/_gen_yaml_2shot +26 -0
  31. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/act_reach.yaml +23 -0
  32. lm-evaluation-harness/lm_eval/tasks/acpbench/gen_2shot_with_pddl/land.yaml +23 -0
  33. lm-evaluation-harness/lm_eval/tasks/acpbench/mcq_cot_2shot/just.yaml +12 -0
  34. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_yaml +33 -0
  35. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_4/afrimgsm_zul.yaml +7 -0
  36. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sna.yaml +7 -0
  37. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_sot.yaml +7 -0
  38. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_twi.yaml +6 -0
  39. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_vai.yaml +6 -0
  40. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yaml +33 -0
  41. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_yor.yaml +6 -0
  42. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct/prompt_5/afrimgsm_zul.yaml +6 -0
  43. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lin.yaml +4 -0
  44. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_lug.yaml +4 -0
  45. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_swa.yaml +4 -0
  46. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_twi.yaml +4 -0
  47. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_vai.yaml +4 -0
  48. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_wol.yaml +4 -0
  49. lm-evaluation-harness/lm_eval/tasks/afrimgsm/direct_cot/prompt_1/afrimgsm_cot_xho.yaml +4 -0
  50. 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