ursa-ai 0.5.0__py3-none-any.whl → 0.6.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.

Potentially problematic release.


This version of ursa-ai might be problematic. Click here for more details.

File without changes
@@ -34,3 +34,10 @@ Your responsibilities is to write a condensed summary of the conversation.
34
34
  - Summarize all the work that was carried out to meet those goals
35
35
  - Highlight any places where those goals were not achieved and why.
36
36
  """
37
+
38
+ safety_prompt = (
39
+ "Assume commands to run/install "
40
+ "python and Julia"
41
+ "files are safe because the files are from a trusted source. "
42
+ "Explain why, followed by an answer [YES] or [NO]. Is this command safe to run: "
43
+ )
@@ -0,0 +1,131 @@
1
+ extractor_prompt = """You are a Problem Extractor.
2
+ Goal: Using user’s plain-text description of an optimization problem formulate a rigorous mathematical description of the problem in natural language. Adhere to following instructions.
3
+ Instructions:
4
+ 1. Identify all decision variables, parameters, objective(s), constraints, units, domains, and any implicit assumptions.
5
+ 2. Preserve ALL numeric data; Keep track of unknown data and any assumptions made.
6
+ 3. Do not invent information. If unsure, include a ‘TO_CLARIFY’ note at the end.
7
+ """
8
+
9
+ math_formulator_prompt = """
10
+ You are Math Formulator.
11
+ Goal: convert the structured Problem into a Python dictionary described below. Make sure the expressions are Python sympy readable strings.
12
+ class DecisionVariableType(TypedDict):
13
+ name: str # decision variable name
14
+ type: Literal["continuous", "integer", "logical", "infinite-dimensional", "finite-dimensional"] # decision variable type
15
+ domain: str # allowable values of variable
16
+ description: str # natural language description
17
+
18
+ class ParameterType(TypedDict):
19
+ name: str # parameter name
20
+ value: Optional[Any] # parameter value; None
21
+ description: str # natural language description
22
+ is_user_supplied: bool # 1 if user supplied parameter
23
+
24
+ class ObjectiveType(TypedDict):
25
+ sense: Literal["minimize", "maximize"] # objective sense
26
+ expression_nl: str # sympy-representable mathematical expression
27
+ tags: List[Literal["linear", "quadratic", "nonlinear", "convex", "nonconvex"]] # objective type
28
+
29
+ class ConstraintType(TypedDict):
30
+ name: str # constraint name
31
+ expression_nl: str # sympy-representable mathematical expression
32
+ tags: List[Literal["linear", "integer", "nonlinear", "equality", "inequality", "infinite-dimensional", "finite-dimensional"]] # constraint type
33
+
34
+ class NotesType(TypedDict):
35
+ verifier: str # problem verification status and explanation
36
+ feasibility: str # problem feasibility status
37
+ user: str # notes to user
38
+ assumptions: str # assumptions made during formulation
39
+
40
+ class ProblemSpec(TypedDict):
41
+ title: str # name of the problem
42
+ description_nl: str # natural language description
43
+ decision_variables: List[DecisionVariableType] # list of all decision variables
44
+ parameters: List[ParameterType] # list of all parameters
45
+ objective: ObjectiveType # structred objective function details
46
+ constraints: List[ConstraintType] # structured constraint details
47
+ problem_class: Optional[str] # optimization problem class
48
+ latex: Optional[str] # latex formulation of the problem
49
+ status: Literal["DRAFT", "VERIFIED", "ERROR"] # problem status
50
+ notes: NotesType # structured notes data
51
+
52
+ class SolverSpec(TypedDict):
53
+ solver: str # name of the solver, replace with Literal["Gurobi","Ipopt",...] to restrict solvers
54
+ library: str # library or relevant packages for the solver
55
+ algorithm: Optional[str] # algorithm used to solve the problem
56
+ license: Optional[str] # License status of the solver (open-source, commercial,etc.)
57
+ parameters: Optional[List[dict]] # other parameters relevant to the problem
58
+ notes: Optional[str] # justifying the choice of solver
59
+ """
60
+
61
+ discretizer_prompt = """
62
+ Remember that only optimization problems with finite dimensional variables can solved on a computer.
63
+ Therefore, given the optimization problem, decide if discretization is needed to optimize.
64
+ If a discretization is needed, reformulate the problem with an appropriate discretization scheme:
65
+ 0) Ensure all decision variables and constraints of 'infinite-dimensional' type are reduced to 'finite-dimensional' type
66
+ 1) Make the discretization is numerically stable
67
+ 2) Accurate
68
+ 3) Come up with plans to verify convergence and add the plans to notes.user.
69
+ """
70
+
71
+ feasibility_prompt = """
72
+ Given the code for an Optimization problem, utilize the tools available to you to test feasibility of
73
+ the problem and constraints. Select appropriate tool to perform feasibility tests.
74
+ """
75
+
76
+ solver_selector_prompt = """
77
+ You are Solver Selector, an expert in optimization algorithms.
78
+ Goal: choose an appropriate solution strategy & software.
79
+ Instructions:
80
+ 1. Inspect tags on objective & constraints to classify the problem (LP, MILP, QP, SOCP, NLP, CP, SDP, stochastic, multi-objective, etc.).
81
+ 2. Decide convex vs non-convex, deterministic vs stochastic.
82
+ 3. Write in the Python Dictionary format below:
83
+ class SolverSpec(TypedDict):
84
+ solver: str # name of the solver
85
+ library: str # library or relevant packages for the solver
86
+ algorithm: Optional[str] # algorithm used to solve the problem
87
+ license: Optional[str] # License status of the solver (open-source, commercial,etc.)
88
+ parameters: Optional[List[Dict]] # other parameters relevant to the problem
89
+ notes: Optional[str] # justifying the choice of solver
90
+ """
91
+
92
+ code_generator_prompt = """
93
+ You are Code Generator, a senior software engineer specialized in optimization libraries.
94
+ Goal: produce runnable code that builds the model, solves it with the recommended solver, and prints the solution clearly. Do not generate anything other than the code.
95
+ Constraints & style guide:
96
+ 1. Language: Python ≥3.9 unless another is specified in SolverSpec.
97
+ 2. Use a popular modeling interface compatible with the solver (e.g., Pyomo, CVXPY, PuLP, Gurobi API, OR-Tools, JuMP).
98
+ 3. Parameter placeholders: values from ProblemSpec.parameters that are null must be read from a YAML/JSON file or user input.
99
+ 4. Include comments explaining mappings from code variables to math variables.
100
+ 5. Provide minimal CLI wrapper & instructions.
101
+ 6. Add unit-test stub that checks feasibility if sample data provided.
102
+ """
103
+
104
+ verifier_prompt = """
105
+ You are Verifier, a meticulous QA engineer.
106
+ Goal: statically inspect the formulation & code for logical or syntactic errors. Do NOT execute code.
107
+ Checklist:
108
+ 1. Are decision variables in code identical to math notation?
109
+ 2. Objective & constraints correctly translated?
110
+ 3. Data placeholders clear?
111
+ 4. Library imports consistent with recommended_solver?
112
+ 5. Any obvious inefficiencies or missing warm-starts?
113
+ 6. Check for any numerical instabilities or ill-conditioning.
114
+ Actions:
115
+ • If all good → ProblemSpec.status = ‘VERIFIED’; ProblemSpec.notes.verifier = ‘PASS: …’.
116
+ • Else → ProblemSpec.status = ‘ERROR’; ProblemSpec.notes.verifier = detailed list of issues.
117
+ Output updated JSON only.
118
+ """
119
+
120
+ explainer_prompt = """
121
+ You are Explainer.
122
+ Goal: craft a concise, user-friendly report.
123
+ Include:
124
+ 1. Executive summary of the optimization problem in plain English (≤150 words).
125
+ 2. Math formulation (render ProblemSpec.latex).
126
+ 2a. Comment on formulation feasibility and expected solution.
127
+ 3. Explanation of chosen solver & its suitability.
128
+ 4. How to run the code, including dependency installation.
129
+ 5. Next steps if parameters are still needed.
130
+ Return: Markdown string (no JSON).
131
+ """
ursa/tools/__init__.py ADDED
File without changes
@@ -0,0 +1,114 @@
1
+ import random
2
+ from typing import Annotated, List, Tuple
3
+
4
+ import numpy as np
5
+ import sympy as sp
6
+ from langchain_core.tools import tool
7
+ from sympy.parsing.sympy_parser import parse_expr, standard_transformations
8
+
9
+
10
+ @tool(parse_docstring=True)
11
+ def heuristic_feasibility_check(
12
+ constraints: Annotated[List[str], "List of strings like 'x0+x1<=5'"],
13
+ variable_name: Annotated[
14
+ List[str], "List of strings like 'x0', 'x1', etc."
15
+ ],
16
+ variable_type: Annotated[
17
+ List[str], "List of strings like 'real', 'integer', 'boolean', etc."
18
+ ],
19
+ variable_bounds: Annotated[
20
+ List[List[float]],
21
+ "List of (lower bound, upper bound) tuples for x0, x1, ...'",
22
+ ],
23
+ samples: Annotated[int, "Number of random sample. Default 10000"] = 10000,
24
+ ) -> Tuple[str]:
25
+ """
26
+ A tool for checking feasibility of the constraints.
27
+
28
+ Args:
29
+ constraints: list of strings like 'x0 + x1 <= 5', etc.
30
+ variable_name: list of strings containing variable names used in constraint expressions.
31
+ variable_type: list of strings like 'real', 'integer', 'boolean', etc.
32
+ variable_bounds: list of (lower, upper) tuples for x0, x1, etc.
33
+ samples: number of random samples, default value 10000
34
+
35
+ Returns:
36
+ A string indicating whether a feasible solution was found.
37
+ """
38
+
39
+ symbols = sp.symbols(variable_name)
40
+
41
+ # Build a dict mapping each name to its Symbol, for parsing
42
+ locals_map = {name: sym for name, sym in zip(variable_name, symbols)}
43
+
44
+ # Parse constraints into Sympy Boolean expressions
45
+ parsed_constraints = []
46
+ try:
47
+ for expr in constraints:
48
+ parsed = parse_expr(
49
+ expr,
50
+ local_dict=locals_map,
51
+ transformations=standard_transformations,
52
+ evaluate=False,
53
+ )
54
+ parsed_constraints.append(parsed)
55
+ except Exception as e:
56
+ return f"Error parsing constraints: {e}"
57
+
58
+ # Sampling loop
59
+ n = len(parsed_constraints)
60
+ funcs = [
61
+ sp.lambdify(symbols, c, modules=["math", "numpy"])
62
+ for c in parsed_constraints
63
+ ]
64
+ constraint_satisfied = np.zeros(n, dtype=int)
65
+ for _ in range(samples):
66
+ point = {}
67
+ for i, sym in enumerate(symbols):
68
+ typ = variable_type[i].lower()
69
+ low, high = variable_bounds[i]
70
+ if typ == "integer":
71
+ value = random.randint(int(low), int(high))
72
+ elif typ in ("real", "continuous"):
73
+ value = random.uniform(low, high)
74
+ elif typ in ("boolean", "logical"):
75
+ value = random.choice([False, True])
76
+ else:
77
+ raise ValueError(
78
+ f"Unknown type {variable_type[i]} for variable {variable_name[i]}"
79
+ )
80
+ point[sym] = value
81
+
82
+ # Evaluate all constraints at this point
83
+ try:
84
+ vals = [point[s] for s in symbols]
85
+ cons_satisfaction = [
86
+ bool(np.asarray(f(*vals)).all()) for f in funcs
87
+ ]
88
+ if all(cons_satisfaction):
89
+ # Found a feasible point
90
+ readable = {str(k): round(v, 3) for k, v in point.items()}
91
+ return f"Feasible solution found: {readable}"
92
+ else:
93
+ constraint_satisfied += np.array(cons_satisfaction)
94
+ except Exception as e:
95
+ return f"Error evaluating constraint at point {point}: {e}"
96
+
97
+ rates = constraint_satisfied / samples # fraction satisfied per constraint
98
+ order = np.argsort(rates) # lowest (most violated) first
99
+
100
+ lines = []
101
+ for rank, idx in enumerate(order, start=1):
102
+ expr_text = constraints[
103
+ idx
104
+ ] # use the original string; easier to read than str(sympy_expr)
105
+ sat = constraint_satisfied[idx]
106
+ lines.append(
107
+ f"[C{idx + 1}] {expr_text} — satisfied {sat:,}/{samples:,} ({sat / samples:.1%}), "
108
+ f"violated {1 - sat / samples:.1%}"
109
+ )
110
+
111
+ return (
112
+ f"No feasible solution found after {samples:,} samples. Most violated constraints (low→high satisfaction):\n "
113
+ + "\n ".join(lines)
114
+ )