text2model 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- text2model/__init__.py +1 -0
- text2model/grammar.mzn +277 -0
- text2model/knowledge_graphs/CSPLib_12.ttl +41 -0
- text2model/knowledge_graphs/CSPLib_14.ttl +51 -0
- text2model/knowledge_graphs/CSPLib_2.ttl +59 -0
- text2model/knowledge_graphs/CSPLib_22.ttl +62 -0
- text2model/knowledge_graphs/CSPLib_28.ttl +28 -0
- text2model/knowledge_graphs/CSPLib_30.ttl +72 -0
- text2model/knowledge_graphs/CSPLib_34.ttl +52 -0
- text2model/knowledge_graphs/CSPLib_39.ttl +51 -0
- text2model/knowledge_graphs/CSPLib_54.ttl +50 -0
- text2model/knowledge_graphs/CSPLib_6.ttl +36 -0
- text2model/knowledge_graphs/CSPLib_8.ttl +53 -0
- text2model/knowledge_graphs/LPWP_128.ttl +79 -0
- text2model/knowledge_graphs/LPWP_232.ttl +95 -0
- text2model/knowledge_graphs/LPWP_53.ttl +58 -0
- text2model/knowledge_graphs/LPWP_66.ttl +52 -0
- text2model/knowledge_graphs/LPWP_85.ttl +95 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_1.ttl +34 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_18.ttl +59 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_2.ttl +43 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_28.ttl +42 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_31.ttl +44 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_4.ttl +48 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_5.ttl +40 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_6.ttl +61 -0
- text2model/knowledge_graphs/OR_LP_IP_Scheduling_problem_8.ttl +54 -0
- text2model/knowledge_graphs/combinatorial_problem_1.ttl +48 -0
- text2model/knowledge_graphs/combinatorial_problem_31.ttl +43 -0
- text2model/knowledge_graphs/combinatorial_problem_32.ttl +39 -0
- text2model/knowledge_graphs/combinatorial_problem_7.ttl +52 -0
- text2model/knowledge_graphs/complexor_AircraftAssignment.ttl +73 -0
- text2model/knowledge_graphs/complexor_AircraftLanding.ttl +53 -0
- text2model/knowledge_graphs/complexor_CFLP.ttl +43 -0
- text2model/knowledge_graphs/complexor_CapacitatedWarehouseLocation.ttl +59 -0
- text2model/knowledge_graphs/complexor_CarSelection.ttl +30 -0
- text2model/knowledge_graphs/complexor_TSP.ttl +52 -0
- text2model/knowledge_graphs/complexor_VRPTW.ttl +67 -0
- text2model/knowledge_graphs/nlp4lp_1.ttl +56 -0
- text2model/knowledge_graphs/nlp4lp_10.ttl +46 -0
- text2model/knowledge_graphs/nlp4lp_11.ttl +57 -0
- text2model/knowledge_graphs/nlp4lp_12.ttl +48 -0
- text2model/knowledge_graphs/nlp4lp_13.ttl +48 -0
- text2model/knowledge_graphs/nlp4lp_14.ttl +82 -0
- text2model/knowledge_graphs/nlp4lp_15.ttl +60 -0
- text2model/knowledge_graphs/nlp4lp_16.ttl +64 -0
- text2model/knowledge_graphs/nlp4lp_17.ttl +72 -0
- text2model/knowledge_graphs/nlp4lp_18.ttl +64 -0
- text2model/knowledge_graphs/nlp4lp_19.ttl +98 -0
- text2model/knowledge_graphs/nlp4lp_2.ttl +103 -0
- text2model/knowledge_graphs/nlp4lp_20.ttl +129 -0
- text2model/knowledge_graphs/nlp4lp_21.ttl +65 -0
- text2model/knowledge_graphs/nlp4lp_22.ttl +72 -0
- text2model/knowledge_graphs/nlp4lp_23.ttl +63 -0
- text2model/knowledge_graphs/nlp4lp_24.ttl +99 -0
- text2model/knowledge_graphs/nlp4lp_25.ttl +77 -0
- text2model/knowledge_graphs/nlp4lp_27.ttl +75 -0
- text2model/knowledge_graphs/nlp4lp_28.ttl +50 -0
- text2model/knowledge_graphs/nlp4lp_29.ttl +63 -0
- text2model/knowledge_graphs/nlp4lp_3.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_30.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_31.ttl +35 -0
- text2model/knowledge_graphs/nlp4lp_32.ttl +33 -0
- text2model/knowledge_graphs/nlp4lp_33.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_34.ttl +67 -0
- text2model/knowledge_graphs/nlp4lp_35.ttl +75 -0
- text2model/knowledge_graphs/nlp4lp_36.ttl +56 -0
- text2model/knowledge_graphs/nlp4lp_37.ttl +52 -0
- text2model/knowledge_graphs/nlp4lp_38.ttl +55 -0
- text2model/knowledge_graphs/nlp4lp_39.ttl +58 -0
- text2model/knowledge_graphs/nlp4lp_4.ttl +64 -0
- text2model/knowledge_graphs/nlp4lp_40.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_41.ttl +62 -0
- text2model/knowledge_graphs/nlp4lp_42.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_43.ttl +98 -0
- text2model/knowledge_graphs/nlp4lp_44.ttl +41 -0
- text2model/knowledge_graphs/nlp4lp_45.ttl +56 -0
- text2model/knowledge_graphs/nlp4lp_46.ttl +35 -0
- text2model/knowledge_graphs/nlp4lp_47.ttl +68 -0
- text2model/knowledge_graphs/nlp4lp_48.ttl +74 -0
- text2model/knowledge_graphs/nlp4lp_49.ttl +66 -0
- text2model/knowledge_graphs/nlp4lp_5.ttl +69 -0
- text2model/knowledge_graphs/nlp4lp_50.ttl +51 -0
- text2model/knowledge_graphs/nlp4lp_51.ttl +53 -0
- text2model/knowledge_graphs/nlp4lp_52.ttl +51 -0
- text2model/knowledge_graphs/nlp4lp_53.ttl +55 -0
- text2model/knowledge_graphs/nlp4lp_54.ttl +74 -0
- text2model/knowledge_graphs/nlp4lp_55.ttl +50 -0
- text2model/knowledge_graphs/nlp4lp_56.ttl +67 -0
- text2model/knowledge_graphs/nlp4lp_57.ttl +59 -0
- text2model/knowledge_graphs/nlp4lp_58.ttl +65 -0
- text2model/knowledge_graphs/nlp4lp_6.ttl +63 -0
- text2model/knowledge_graphs/nlp4lp_60.ttl +36 -0
- text2model/knowledge_graphs/nlp4lp_61.ttl +83 -0
- text2model/knowledge_graphs/nlp4lp_62.ttl +44 -0
- text2model/knowledge_graphs/nlp4lp_63.ttl +49 -0
- text2model/knowledge_graphs/nlp4lp_64.ttl +41 -0
- text2model/knowledge_graphs/nlp4lp_65.ttl +78 -0
- text2model/knowledge_graphs/nlp4lp_66.ttl +51 -0
- text2model/knowledge_graphs/nlp4lp_67.ttl +40 -0
- text2model/knowledge_graphs/nlp4lp_7.ttl +53 -0
- text2model/knowledge_graphs/nlp4lp_8.ttl +51 -0
- text2model/knowledge_graphs/nlp4lp_9.ttl +58 -0
- text2model/knowledge_graphs/non_linear_problem_10.ttl +57 -0
- text2model/knowledge_graphs/non_linear_problem_15.ttl +55 -0
- text2model/knowledge_graphs/non_linear_problem_23.ttl +42 -0
- text2model/knowledge_graphs/non_linear_problem_9.ttl +61 -0
- text2model/knowledge_graphs/puzzles_problem_1.ttl +64 -0
- text2model/knowledge_graphs/puzzles_problem_11.ttl +65 -0
- text2model/knowledge_graphs/puzzles_problem_12.ttl +79 -0
- text2model/knowledge_graphs/puzzles_problem_13.ttl +50 -0
- text2model/knowledge_graphs/puzzles_problem_31.ttl +62 -0
- text2model/main.py +771 -0
- text2model/prompts/assembler_prompt.txt +104 -0
- text2model/prompts/code_stitching_prompt.txt +49 -0
- text2model/prompts/code_validation_prompt.txt +36 -0
- text2model/prompts/constraint_generation_prompt.txt +35 -0
- text2model/prompts/cot_prompt.txt +45 -0
- text2model/prompts/global_constraint_prompts/all_different_prompt.txt +67 -0
- text2model/prompts/global_constraint_prompts/count_prompt.txt +75 -0
- text2model/prompts/global_constraint_prompts/cumulative_prompt.txt +88 -0
- text2model/prompts/global_constraint_prompts/global_cardinality_prompt.txt +76 -0
- text2model/prompts/global_constraint_prompts/increasing_prompt.txt +81 -0
- text2model/prompts/global_constraint_prompts/lex_less_prompt.txt +68 -0
- text2model/prompts/grammar_validation_prompt.txt +18 -0
- text2model/prompts/kg_code_generation_prompt.txt +20 -0
- text2model/prompts/kg_generation_prompt.txt +40 -0
- text2model/prompts/objective_generation_prompt.txt +34 -0
- text2model/prompts/parameter_and_variable_generation_prompt.txt +35 -0
- text2model/utils.py +350 -0
- text2model-0.1.0.dist-info/METADATA +221 -0
- text2model-0.1.0.dist-info/RECORD +135 -0
- text2model-0.1.0.dist-info/WHEEL +5 -0
- text2model-0.1.0.dist-info/entry_points.txt +2 -0
- text2model-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
You are a constraint modeling assistant specialized in MiniZinc, especially in detecting and modeling lexicographic ordering constraints such as `lex_less`.
|
|
2
|
+
|
|
3
|
+
## Task
|
|
4
|
+
Given a **problem description** and optional **input data**, decide whether the problem **requires a lexicographic constraint** (either `lex_less` or `lex_lesseq`).
|
|
5
|
+
|
|
6
|
+
- If it does, generate **only MiniZinc code** specifying the appropriate lexicographic constraint and its relevant variables.
|
|
7
|
+
- Do **not** generate any other constraints.
|
|
8
|
+
- If it does **not** require lexicographic ordering, return `FALSE` and a brief reason.
|
|
9
|
+
|
|
10
|
+
The MiniZinc code should assume that the data needed, will be provided in **input data** through a .dzn file.
|
|
11
|
+
|
|
12
|
+
## What is a lexicographic constraint?
|
|
13
|
+
Lexicographic constraints (`lex_less`, `lex_lesseq`) compare two sequences (arrays) of variables element-wise in order. They are used to:
|
|
14
|
+
- Eliminate symmetric solutions
|
|
15
|
+
- Impose ordering among configurations or schedules
|
|
16
|
+
- Maintain canonical form
|
|
17
|
+
|
|
18
|
+
## Detection Guide: Common Clues
|
|
19
|
+
Use `lex_less` or `lex_lesseq` when the problem includes language such as:
|
|
20
|
+
- "Configuration A must be before configuration B"
|
|
21
|
+
- "Sort lexicographically"
|
|
22
|
+
- "Solution vectors must be in order"
|
|
23
|
+
- "Break symmetry by comparing rows"
|
|
24
|
+
|
|
25
|
+
Heuristic:
|
|
26
|
+
- Use `lex_less(a, b)` if strict ordering is required.
|
|
27
|
+
- Use `lex_lesseq(a, b)` for non-strict (A ≤ B lexicographically).
|
|
28
|
+
|
|
29
|
+
## Output Format
|
|
30
|
+
- If constraint is needed, output **MiniZinc code only**, using `include "globals.mzn";`
|
|
31
|
+
- If not needed, return:
|
|
32
|
+
```text
|
|
33
|
+
FALSE
|
|
34
|
+
Reason: <short explanation>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## MiniZinc Snippets
|
|
38
|
+
```mzn
|
|
39
|
+
include "globals.mzn";
|
|
40
|
+
|
|
41
|
+
int: n;
|
|
42
|
+
array[1..n] of var int: a;
|
|
43
|
+
array[1..n] of var int: b;
|
|
44
|
+
|
|
45
|
+
constraint lex_less(a, b);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
### ✅ Lex ordering in a matrix
|
|
50
|
+
"Rows in the matrix must be lexicographically ordered"
|
|
51
|
+
```mzn
|
|
52
|
+
include "globals.mzn";
|
|
53
|
+
|
|
54
|
+
int: r = 3;
|
|
55
|
+
int: c = 4;
|
|
56
|
+
array[1..r, 1..c] of var 0..1: mat;
|
|
57
|
+
|
|
58
|
+
constraint forall(i in 1..r-1)(
|
|
59
|
+
lex_lesseq([mat[i,j] | j in 1..c], [mat[i+1,j] | j in 1..c])
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### ❌ Not a lexicographic constraint
|
|
64
|
+
"Ensure each item is assigned a unique time slot"
|
|
65
|
+
```text
|
|
66
|
+
FALSE
|
|
67
|
+
Reason: This is a uniqueness constraint — use all_different, not lex_lesseq or lex_less.
|
|
68
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
You are given a MiniZinc model that has syntax errors. Your task is to fix these syntax errors using the MiniZinc grammar specification provided below.
|
|
2
|
+
|
|
3
|
+
**Problem Description:**
|
|
4
|
+
{problem_description}
|
|
5
|
+
|
|
6
|
+
**Input Data:**
|
|
7
|
+
{input_data}
|
|
8
|
+
|
|
9
|
+
**Current Code with Syntax Errors:**
|
|
10
|
+
{current_code}
|
|
11
|
+
|
|
12
|
+
**Error message after execution:**
|
|
13
|
+
{syntax_error_message}
|
|
14
|
+
|
|
15
|
+
**MiniZinc Grammar Specification:**
|
|
16
|
+
{minizinc_grammar}
|
|
17
|
+
|
|
18
|
+
Using the grammar specification above, carefully analyze the syntax errors and correct the code. Please provide the corrected MiniZinc code that adheres to the grammar specification and resolves all syntax errors. Output only the complete and corrected MiniZinc code without additional comments.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are an expert MiniZinc developer.
|
|
2
|
+
|
|
3
|
+
Generate Minizinc code from using the following information:
|
|
4
|
+
|
|
5
|
+
1. Problem Description: A formal description describing the optimization problem.
|
|
6
|
+
2. Knowledge Graph: Detailing Parameters, Variables, Constraints and Objective.
|
|
7
|
+
3. Input Data: The MiniZinc code should assume that the data needed, will be provided in a specific format through a .dzn file, so the generated code should assume the same names defined in the input data nomenclature.
|
|
8
|
+
|
|
9
|
+
Please do not generate any other token, except the MiniZinc code.
|
|
10
|
+
|
|
11
|
+
Problem Description:
|
|
12
|
+
{problem_description}
|
|
13
|
+
|
|
14
|
+
Knowledge Graph:
|
|
15
|
+
```
|
|
16
|
+
{knowledge_graph}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Input Data:
|
|
20
|
+
{input_data}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Given an optimization problem description and the nomenclature to be used for parameters.
|
|
2
|
+
|
|
3
|
+
Please generate a knowledge-graph.
|
|
4
|
+
|
|
5
|
+
Problem Description:
|
|
6
|
+
{problem_description}
|
|
7
|
+
|
|
8
|
+
Input Data:
|
|
9
|
+
{input_data}
|
|
10
|
+
|
|
11
|
+
Steps to Generate a Knowledge Graph (KG):
|
|
12
|
+
|
|
13
|
+
1. Identify Parameters:
|
|
14
|
+
- Extract each parameter individually, specifying its type and name.
|
|
15
|
+
- Ensure that parameter names are aligned with the predefined nomenclature.
|
|
16
|
+
- Record any explicit bounds for parameters, if specified.
|
|
17
|
+
- Validate against the problem description to ensure that all relevant parameters are included, checking for any implicitly stated ones.
|
|
18
|
+
|
|
19
|
+
2. Identify Variables:
|
|
20
|
+
- Identify each variable, noting their types and names.
|
|
21
|
+
- Align variable names with the nomenclature provided, ensuring consistency.
|
|
22
|
+
- Determine and document any bounds (explicit or inferred) for the variables.
|
|
23
|
+
- Cross-verify with the problem description to confirm all necessary variables are accounted for, including those not explicitly mentioned in the nomenclature.
|
|
24
|
+
|
|
25
|
+
3. Identify Constraints:
|
|
26
|
+
- Detail each constraint separately, recording its description, formula, and associated variables.
|
|
27
|
+
- Classify constraints based on their nature (e.g., linear, nonlinear, global etc..).
|
|
28
|
+
- Highlight if any constraints are global, impacting multiple variables or conditions across the model.
|
|
29
|
+
|
|
30
|
+
4. Identify Objective:
|
|
31
|
+
- Clearly define the objective function, including its description, mathematical representation, and the variables it affects.
|
|
32
|
+
- Note the type of optimization (minimization, maximization, satisfy) and any relevant constraints tied to the objective.
|
|
33
|
+
- Ensure the objective aligns with the overall goal of the optimization problem as described.
|
|
34
|
+
|
|
35
|
+
5. Generate Turtle (TTL) Representation:
|
|
36
|
+
- Construct the Turtle format representation using the gathered information on parameters, variables, constraints, and the objective.
|
|
37
|
+
- Sequentially organize the definitions of parameters, variables, constraints, and the objective within the Turtle file.
|
|
38
|
+
- Avoid generalizing the problem statement; focus on defining each component distinctly as outlined in the steps.
|
|
39
|
+
|
|
40
|
+
Please do not generate any other token other than the knowledge graph itself. No titles, description or markup necessary.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
You are an expert MiniZinc developer.
|
|
2
|
+
|
|
3
|
+
Generate MiniZinc code for the Objective from a given problem description with additional information about the parameters, variables and constraints provided.
|
|
4
|
+
|
|
5
|
+
Given the Parameters, Variables, and Constraints sections of the code, generate only the objective.
|
|
6
|
+
|
|
7
|
+
When generating the code, follow this format:
|
|
8
|
+
|
|
9
|
+
```minizinc
|
|
10
|
+
% Objective
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Also, make sure to follow the following principles when generating the code:
|
|
14
|
+
|
|
15
|
+
**General Principles**:
|
|
16
|
+
1. Ensure there is only one objective, which will be a maximization, minimization, or a satisfy problem. Do not forget the `solve` keyword.
|
|
17
|
+
2. Ensure the objective function aligns with the problem description.
|
|
18
|
+
3. Verify the correct usage of variables, parameters and constraints in the objective function.
|
|
19
|
+
|
|
20
|
+
**Problem Description**:
|
|
21
|
+
{problem_description}
|
|
22
|
+
|
|
23
|
+
**Input Data**:
|
|
24
|
+
{input_data}
|
|
25
|
+
|
|
26
|
+
**Parameters and Variables**:
|
|
27
|
+
```minizinc
|
|
28
|
+
{parameters_and_variables}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Constraints**:
|
|
32
|
+
```minizinc
|
|
33
|
+
{constraints}
|
|
34
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
You are an expert MiniZinc developer.
|
|
2
|
+
|
|
3
|
+
Generate MiniZinc code for the Parameters and Variables from a given problem description with additional information about input data provided.
|
|
4
|
+
|
|
5
|
+
The MiniZinc code should assume that the data needed will be provided in a specific format through a .dzn file, so the generated code should assume the same names/data-types defined in the **input data nomenclature and examples**.
|
|
6
|
+
|
|
7
|
+
When generating the code, follow this format:
|
|
8
|
+
|
|
9
|
+
```minizinc
|
|
10
|
+
% Parameters
|
|
11
|
+
|
|
12
|
+
% Variables
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Also, make sure to follow the following principles when generating the code:
|
|
16
|
+
|
|
17
|
+
**General Principles**:
|
|
18
|
+
1. The generated code should assume that data will be provided via a ".dzn" file. Do not declare values directly from the input data nomenclature and examples within the MiniZinc model.
|
|
19
|
+
2. Adhere to the input data and examples precisely when declaring input parameter names and their data types.
|
|
20
|
+
3. Use bounded variables whenever possible. If bounds are explicit (e.g., non-negative), include them as constraints.
|
|
21
|
+
4. When defining arrays of variables, ensure bounds are integers. Apply element-wise constraints in a separate constraint block if bounds depend on array elements.
|
|
22
|
+
5. When defining arrays of variables, ensure bounding constraints are applied separately rather than during initialization to avoid type mismatches.
|
|
23
|
+
6. Define explicit bounds for all variables used in linear expressions, either in their declaration or through additional constraints.
|
|
24
|
+
7. Declare all parameters and sets before using them in other declarations to avoid circular dependencies and ordering issues.
|
|
25
|
+
8. Declare all identifiers (such as indices or ranges like `n`) before using them in any array or parameter declarations.
|
|
26
|
+
9. Ensure that all indices and sets used in parameter and variable declarations are declared beforehand.
|
|
27
|
+
10. When declaring variables, ensure they are appropriately typed (e.g., `int`, `float`).
|
|
28
|
+
11. Variables should have meaningful names related to the problem description.
|
|
29
|
+
12. Include comments to briefly describe each parameter and variable for clarity.
|
|
30
|
+
|
|
31
|
+
**Problem Description**:
|
|
32
|
+
{problem_description}
|
|
33
|
+
|
|
34
|
+
**Input Data**:
|
|
35
|
+
{input_data}
|
text2model/utils.py
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
import openai
|
|
11
|
+
from langchain_ollama import ChatOllama
|
|
12
|
+
|
|
13
|
+
# Global OpenAI API configuration
|
|
14
|
+
API_CONFIG = {
|
|
15
|
+
'model': 'gpt-4',
|
|
16
|
+
'temperature': 0,
|
|
17
|
+
'max_tokens': 4096,
|
|
18
|
+
'sleep_time': 3
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Package installation directory — used to locate bundled data files
|
|
22
|
+
_PACKAGE_DIR = Path(__file__).parent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _resolve_path(rel_path: str) -> Path:
|
|
26
|
+
"""Resolve a data-file path: try CWD first, then the installed package directory."""
|
|
27
|
+
p = Path(rel_path)
|
|
28
|
+
if p.exists():
|
|
29
|
+
return p
|
|
30
|
+
pkg_p = _PACKAGE_DIR / rel_path
|
|
31
|
+
if pkg_p.exists():
|
|
32
|
+
return pkg_p
|
|
33
|
+
return p # Return original; callers handle the missing-file case
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extract_code_blocks(text: str) -> str:
|
|
37
|
+
"""Extract code blocks from markdown-formatted text"""
|
|
38
|
+
pattern = re.compile(r'```(?:\w+)?\n(.*?)\n```', re.DOTALL)
|
|
39
|
+
matches = pattern.findall(text)
|
|
40
|
+
return matches[0] if matches else text
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def extract_global_constraint(text):
|
|
44
|
+
first_line = text.splitlines()[0]
|
|
45
|
+
return re.findall(r'`(.*?)`', first_line)[0]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def call_api(client, model: str, prompt: str) -> Optional[str]:
|
|
49
|
+
if model in ["gpt-4", "gpt-4o", "gpt-5.2"]:
|
|
50
|
+
solution = call_openai_api(client, prompt)
|
|
51
|
+
print(solution)
|
|
52
|
+
return solution
|
|
53
|
+
else:
|
|
54
|
+
solution = call_ollama_api(client, prompt)
|
|
55
|
+
return solution
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def call_openai_api(client: openai.OpenAI, prompt: str) -> Optional[str]:
|
|
59
|
+
"""Call OpenAI API with the given prompt"""
|
|
60
|
+
try:
|
|
61
|
+
# Map model names - use o3 for gpt-4o, gpt-5.2 uses its own name
|
|
62
|
+
model_name = API_CONFIG['model']
|
|
63
|
+
if model_name == 'gpt-4o':
|
|
64
|
+
model_name = 'o3-2025-04-16'
|
|
65
|
+
# gpt-5.2 uses its own name directly
|
|
66
|
+
|
|
67
|
+
params = {
|
|
68
|
+
"model": model_name,
|
|
69
|
+
"messages": [{"role": "user", "content": prompt}]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Add temperature and max_tokens only for non-reasoning models
|
|
73
|
+
# Reasoning models: gpt-4o (mapped to o3), gpt-5.2
|
|
74
|
+
if API_CONFIG['model'] not in ['gpt-4o', 'gpt-5.2']:
|
|
75
|
+
params["temperature"] = API_CONFIG['temperature']
|
|
76
|
+
params["max_tokens"] = API_CONFIG['max_tokens']
|
|
77
|
+
|
|
78
|
+
completion = client.chat.completions.create(**params)
|
|
79
|
+
result = completion.choices[0].message.content.strip()
|
|
80
|
+
return extract_code_blocks(result)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"Error calling OpenAI API: {e}")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def call_ollama_api(client: ChatOllama, prompt: str) -> Optional[str]:
|
|
87
|
+
"""Call ChatOllama with the given prompt"""
|
|
88
|
+
try:
|
|
89
|
+
messages = [
|
|
90
|
+
{"role": "user", "content": prompt},
|
|
91
|
+
]
|
|
92
|
+
result = client.invoke(messages).content.strip()
|
|
93
|
+
return extract_code_blocks(result)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print(f"Error calling ChatOllama: {e}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def check_syntax(mzn_code: str, dzn_data: str, timeout: int = 60) -> Optional[str]:
|
|
100
|
+
"""Check MiniZinc syntax and return error message if any"""
|
|
101
|
+
with tempfile.NamedTemporaryFile(suffix='.mzn', mode='w', delete=False) as mzn_f:
|
|
102
|
+
mzn_f.write(mzn_code)
|
|
103
|
+
temp_mzn = mzn_f.name
|
|
104
|
+
|
|
105
|
+
temp_dzn = None
|
|
106
|
+
try:
|
|
107
|
+
cmd = [shutil.which("minizinc") or "minizinc", temp_mzn]
|
|
108
|
+
if dzn_data and dzn_data.strip():
|
|
109
|
+
with tempfile.NamedTemporaryFile(suffix='.dzn', mode='w', delete=False) as dzn_f:
|
|
110
|
+
dzn_f.write(dzn_data)
|
|
111
|
+
temp_dzn = dzn_f.name
|
|
112
|
+
cmd.append(temp_dzn)
|
|
113
|
+
|
|
114
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
|
115
|
+
if result.returncode != 0:
|
|
116
|
+
return result.stderr
|
|
117
|
+
return None
|
|
118
|
+
except subprocess.TimeoutExpired:
|
|
119
|
+
return f"MiniZinc execution timed out after {timeout} seconds"
|
|
120
|
+
except Exception as e:
|
|
121
|
+
return f"Error checking syntax: {str(e)}"
|
|
122
|
+
finally:
|
|
123
|
+
if os.path.exists(temp_mzn):
|
|
124
|
+
os.remove(temp_mzn)
|
|
125
|
+
if temp_dzn and os.path.exists(temp_dzn):
|
|
126
|
+
os.remove(temp_dzn)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def parse_dzn_string(dzn_str: str) -> List[Tuple[str, str]]:
|
|
130
|
+
"""Parse dzn string into parameter-value pairs"""
|
|
131
|
+
parameters = re.findall(r'(\w+)\s*=\s*[^;]+;', dzn_str)
|
|
132
|
+
content_list = dzn_str.split("\n")
|
|
133
|
+
valid_lines = [line for line in content_list if '=' in line]
|
|
134
|
+
result = list(zip(parameters, valid_lines))
|
|
135
|
+
return result
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def create_data_nomenclature(input_data: Dict[str, Any], dzn_data: List[Tuple[str, str]]) -> str:
|
|
139
|
+
"""Create data nomenclature section for prompts.
|
|
140
|
+
|
|
141
|
+
Returns empty string if no parameters or no dzn_data.
|
|
142
|
+
"""
|
|
143
|
+
if not dzn_data:
|
|
144
|
+
return ""
|
|
145
|
+
|
|
146
|
+
parameters = input_data.get('parameters', [])
|
|
147
|
+
|
|
148
|
+
if not parameters:
|
|
149
|
+
return ""
|
|
150
|
+
|
|
151
|
+
data_nomenclature = []
|
|
152
|
+
|
|
153
|
+
for idx, param in enumerate(parameters):
|
|
154
|
+
symbol = param['symbol']
|
|
155
|
+
definition = param['definition']
|
|
156
|
+
shape = param['shape']
|
|
157
|
+
|
|
158
|
+
example_line = next(
|
|
159
|
+
(line for param_name, line in dzn_data if param_name == symbol),
|
|
160
|
+
f"{symbol} = N/A;"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
shape_display = f"[{', '.join(map(str, shape))}]" if shape else "scalar"
|
|
164
|
+
|
|
165
|
+
data_nomenclature.append(
|
|
166
|
+
f"{idx + 1}. {symbol}: {definition}\n"
|
|
167
|
+
f"Example: {example_line}\n"
|
|
168
|
+
f"Shape: {shape_display}"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return '\n'.join(data_nomenclature)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def prepare_problem_data(problem: Dict[str, Any]) -> Dict[str, Any]:
|
|
175
|
+
"""Prepare problem data for use in prompts"""
|
|
176
|
+
input_data = ast.literal_eval(problem['input.json'])
|
|
177
|
+
|
|
178
|
+
if problem.get('data.dzn') and problem['data.dzn'].strip():
|
|
179
|
+
dzn_data = parse_dzn_string(problem['data.dzn'])
|
|
180
|
+
else:
|
|
181
|
+
dzn_data = []
|
|
182
|
+
|
|
183
|
+
data_nomenclature = create_data_nomenclature(input_data, dzn_data)
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
'description': input_data['description'],
|
|
187
|
+
'data_nomenclature': data_nomenclature,
|
|
188
|
+
'objective_type': input_data.get('metadata', {}).get('objective', 'unknown'),
|
|
189
|
+
'identifier': input_data.get('metadata', {}).get('identifier', ''),
|
|
190
|
+
'input_data': input_data,
|
|
191
|
+
'dzn_data': dzn_data
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_effective_input_data(problem_data: Dict[str, Any]) -> str:
|
|
196
|
+
"""Get the input data, with instructions if no dzn data exists."""
|
|
197
|
+
if not problem_data['data_nomenclature'].strip():
|
|
198
|
+
return """IMPORTANT: All data and parameters are already included in the problem description above.
|
|
199
|
+
You must embed all data directly in the MiniZinc model - do not expect external .dzn files or assume data will be provided separately.
|
|
200
|
+
Generate MiniZinc code ONLY. Do NOT generate CPOPT, COPT, or any other format even if the problem description mentions it."""
|
|
201
|
+
|
|
202
|
+
return problem_data['data_nomenclature']
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def create_baseline_prompt(problem: Dict[str, Any]) -> str:
|
|
206
|
+
"""Create a baseline prompt for single-stage generation"""
|
|
207
|
+
problem_data = prepare_problem_data(problem)
|
|
208
|
+
effective_input_data = get_effective_input_data(problem_data)
|
|
209
|
+
|
|
210
|
+
if problem_data['data_nomenclature'].strip():
|
|
211
|
+
return f"""You are an expert MiniZinc developer.
|
|
212
|
+
|
|
213
|
+
Generate Minizinc code from a given problem description with additional information about the parameters provided.
|
|
214
|
+
|
|
215
|
+
The MiniZinc code should assume that the data needed, will be provided in a specific format through a .dzn file, so the generated code should assume the same names defined in the input data nomenclature.
|
|
216
|
+
|
|
217
|
+
Please do not generate any other token, except the MiniZinc code.
|
|
218
|
+
|
|
219
|
+
Problem Description:
|
|
220
|
+
{problem_data['description']}
|
|
221
|
+
|
|
222
|
+
Input Data Nomenclature:
|
|
223
|
+
{effective_input_data}
|
|
224
|
+
"""
|
|
225
|
+
else:
|
|
226
|
+
return f"""You are an expert MiniZinc developer.
|
|
227
|
+
|
|
228
|
+
Generate MiniZinc code from the given problem description. All data and parameters are included in the problem description, so embed them directly in your MiniZinc model.
|
|
229
|
+
|
|
230
|
+
IMPORTANT: Generate MiniZinc code ONLY. Do NOT generate CPOPT, COPT, or any other format even if the problem description asks for it.
|
|
231
|
+
|
|
232
|
+
Please do not generate any other token, except the MiniZinc code.
|
|
233
|
+
|
|
234
|
+
Problem Description:
|
|
235
|
+
{problem_data['description']}
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def create_problem_from_text(text: str) -> Dict[str, Any]:
|
|
240
|
+
"""Create a dataset-compatible problem dict from a plain text description."""
|
|
241
|
+
input_data = {
|
|
242
|
+
'description': text,
|
|
243
|
+
'parameters': [],
|
|
244
|
+
'metadata': {
|
|
245
|
+
'objective': 'unknown',
|
|
246
|
+
'identifier': 'user_problem',
|
|
247
|
+
'source': 'user',
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
'input.json': repr(input_data),
|
|
252
|
+
'data.dzn': '',
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def save_solution(output_dir: str, problem_id: str, solution: str) -> None:
|
|
257
|
+
"""Save the generated solution to a file"""
|
|
258
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
259
|
+
output_path = os.path.join(output_dir, f"{problem_id}.mzn")
|
|
260
|
+
with open(output_path, 'w') as f:
|
|
261
|
+
f.write(solution)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def load_file(file_path: str) -> str:
|
|
265
|
+
"""Load a file, resolving the path relative to the package directory if not found locally."""
|
|
266
|
+
p = _resolve_path(file_path)
|
|
267
|
+
try:
|
|
268
|
+
return p.read_text()
|
|
269
|
+
except FileNotFoundError:
|
|
270
|
+
print(f"File not found: {file_path}")
|
|
271
|
+
return ""
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_problem_source(problem):
|
|
275
|
+
"""Extract the source from problem's input.json metadata"""
|
|
276
|
+
try:
|
|
277
|
+
input_data = ast.literal_eval(problem['input.json'])
|
|
278
|
+
return input_data.get('metadata', {}).get('source', None)
|
|
279
|
+
except Exception:
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_problem_identifier(problem, idx):
|
|
284
|
+
"""Extract the identifier from problem's input.json metadata."""
|
|
285
|
+
try:
|
|
286
|
+
input_data = ast.literal_eval(problem['input.json'])
|
|
287
|
+
metadata = input_data.get('metadata', {})
|
|
288
|
+
identifier = metadata.get('identifier', '')
|
|
289
|
+
|
|
290
|
+
if not identifier or not identifier.strip():
|
|
291
|
+
source = metadata.get('source', 'unknown')
|
|
292
|
+
safe_source = re.sub(r'[^\w\-]', '_', source.lower())
|
|
293
|
+
identifier = f"{safe_source}_problem_{idx}"
|
|
294
|
+
elif identifier in ['easy_lp', 'complex_lp']:
|
|
295
|
+
identifier = f"{identifier}_{idx}"
|
|
296
|
+
|
|
297
|
+
return identifier
|
|
298
|
+
except Exception:
|
|
299
|
+
return f"unknown_problem_{idx}"
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_cardinal_ops_subfolder(problem):
|
|
303
|
+
"""Determine the subfolder name for cardinal_operations datasets."""
|
|
304
|
+
try:
|
|
305
|
+
input_data = ast.literal_eval(problem['input.json'])
|
|
306
|
+
metadata = input_data.get('metadata', {})
|
|
307
|
+
source = metadata.get('source', '')
|
|
308
|
+
identifier = metadata.get('identifier', '')
|
|
309
|
+
|
|
310
|
+
if not source.startswith('cardinal_operations'):
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
if source == 'cardinal_operations_mamo':
|
|
314
|
+
if identifier == 'easy_lp':
|
|
315
|
+
return 'easylp'
|
|
316
|
+
elif identifier == 'complex_lp':
|
|
317
|
+
return 'complexlp'
|
|
318
|
+
else:
|
|
319
|
+
return 'mamo'
|
|
320
|
+
elif source == 'cardinal_operations_nl4opt':
|
|
321
|
+
return 'nl4opt'
|
|
322
|
+
elif source == 'cardinal_operations_industryor':
|
|
323
|
+
return 'industryor'
|
|
324
|
+
else:
|
|
325
|
+
suffix = source.replace('cardinal_operations_', '')
|
|
326
|
+
return suffix if suffix else None
|
|
327
|
+
|
|
328
|
+
except Exception:
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def filter_dataset_by_source(dataset, source_filter):
|
|
333
|
+
"""Filter dataset by source field in metadata"""
|
|
334
|
+
def matches_source(problem):
|
|
335
|
+
source = get_problem_source(problem)
|
|
336
|
+
if source is None:
|
|
337
|
+
return False
|
|
338
|
+
return source_filter.lower() in source.lower()
|
|
339
|
+
|
|
340
|
+
return dataset.filter(matches_source)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def get_available_sources(dataset):
|
|
344
|
+
"""Get all unique sources in the dataset"""
|
|
345
|
+
sources = set()
|
|
346
|
+
for problem in dataset:
|
|
347
|
+
source = get_problem_source(problem)
|
|
348
|
+
if source:
|
|
349
|
+
sources.add(source)
|
|
350
|
+
return sorted(sources)
|