oatdb 0.4.0__tar.gz
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.
- oatdb-0.4.0/PKG-INFO +364 -0
- oatdb-0.4.0/README.md +342 -0
- oatdb-0.4.0/oatdb/__init__.py +1111 -0
- oatdb-0.4.0/pyproject.toml +33 -0
oatdb-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: oatdb
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Python client for OAT (Optimization and Analysis Tooling) database
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: optimization,constraint-solving,database
|
|
7
|
+
Author: Rikard Olsson
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Requires-Dist: more-itertools (>=10.8.0,<11.0.0)
|
|
18
|
+
Requires-Dist: pldag (>=0.10.17,<0.11.0)
|
|
19
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# OatDB Python SDK
|
|
23
|
+
|
|
24
|
+
A Python client library for interacting with the OatDB (Optimization and Analysis Tooling) database backend.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- ✅ Full support for all 30 OatDB API functions
|
|
29
|
+
- ✅ Logical operations (AND, OR, XOR, NOT, IMPLY, EQUIV)
|
|
30
|
+
- ✅ Cardinality constraints (AtLeast, AtMost, Equal)
|
|
31
|
+
- ✅ Linear inequality constraints (GeLineq)
|
|
32
|
+
- ✅ Property management
|
|
33
|
+
- ✅ DAG operations (sub, sub_many, validate, ranks)
|
|
34
|
+
- ✅ Constraint propagation
|
|
35
|
+
- ✅ Optimization solver (solve, solve_many)
|
|
36
|
+
- ✅ Node deletion and management
|
|
37
|
+
- ✅ Alias support for named constraints
|
|
38
|
+
- ✅ Type hints for better IDE support
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install oat-python-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or with Poetry:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
poetry add oat-python-sdk
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from oatdb import OatClient, set_primitive, set_property, set_and, sub, solve
|
|
56
|
+
|
|
57
|
+
# Initialize client
|
|
58
|
+
client = OatClient("http://localhost:7061")
|
|
59
|
+
|
|
60
|
+
# Create primitives with bounds [min, max]
|
|
61
|
+
x = set_primitive("x", bound=1j) # [0, 1]
|
|
62
|
+
y = set_primitive("y", bound=10j) # [0, 10]
|
|
63
|
+
|
|
64
|
+
# Add properties
|
|
65
|
+
x_name = set_property(x, "name", "Variable X")
|
|
66
|
+
|
|
67
|
+
# Create constraints
|
|
68
|
+
constraint = set_and([x, y], alias="my_constraint")
|
|
69
|
+
|
|
70
|
+
# Extract DAG and solve
|
|
71
|
+
dag = sub(constraint)
|
|
72
|
+
solution = solve(
|
|
73
|
+
dag=dag,
|
|
74
|
+
objective={
|
|
75
|
+
x: 1,
|
|
76
|
+
y: 2
|
|
77
|
+
},
|
|
78
|
+
assume={
|
|
79
|
+
# Force constraint to be true
|
|
80
|
+
constraint: 1+1j
|
|
81
|
+
},
|
|
82
|
+
maximize=True
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Execute and get results
|
|
86
|
+
result = client.execute(solution)
|
|
87
|
+
print(result)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Core Concepts
|
|
91
|
+
|
|
92
|
+
### Bounds
|
|
93
|
+
|
|
94
|
+
Bounds are represented as complex numbers where:
|
|
95
|
+
- Real part = lower bound
|
|
96
|
+
- Imaginary part = upper bound
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# Bound [0, 1]
|
|
100
|
+
bound = 1j
|
|
101
|
+
|
|
102
|
+
# Bound [5, 10]
|
|
103
|
+
bound = 5 + 10j
|
|
104
|
+
|
|
105
|
+
# Access bounds from solution
|
|
106
|
+
solution_data = result[solution.out]
|
|
107
|
+
x_bounds = solution_data["x"] # [lower, upper] as list
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Function Calls and Execution
|
|
111
|
+
|
|
112
|
+
All operations return `FunctionCall` objects that you execute using the client:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from oatdb import OatClient, set_primitive, set_and, sub
|
|
116
|
+
|
|
117
|
+
client = OatClient("http://localhost:7061")
|
|
118
|
+
|
|
119
|
+
# Create function calls
|
|
120
|
+
x = set_primitive("x", bound=1j)
|
|
121
|
+
y = set_primitive("y", bound=1j)
|
|
122
|
+
constraint = set_and([x, y])
|
|
123
|
+
|
|
124
|
+
# Execute a single operation
|
|
125
|
+
result = client.execute(constraint)
|
|
126
|
+
|
|
127
|
+
# Execute multiple operations
|
|
128
|
+
dag = sub(constraint)
|
|
129
|
+
result = client.execute_many([x, y, constraint, dag])
|
|
130
|
+
|
|
131
|
+
# Access results by the function call's output key
|
|
132
|
+
dag_data = result[dag.out]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Available Functions
|
|
136
|
+
|
|
137
|
+
All functions return `FunctionCall` objects that are executed using `client.execute()` or `client.execute_many()`.
|
|
138
|
+
|
|
139
|
+
### Primitive Operations
|
|
140
|
+
- `set_primitive(id: str, bound: complex = 1j, alias: Optional[str] = None)` - Create a single primitive
|
|
141
|
+
- `set_primitives(ids: List[str], bound: complex = 1j)` - Create multiple primitives
|
|
142
|
+
- `set_property(id: Union[str, FunctionCall], property: str, value: Any)` - Set node property
|
|
143
|
+
|
|
144
|
+
### Logical Operations
|
|
145
|
+
- `set_and(references: List, alias: Optional[str] = None)` - AND operation
|
|
146
|
+
- `set_or(references: List, alias: Optional[str] = None)` - OR operation
|
|
147
|
+
- `set_xor(references: List, alias: Optional[str] = None)` - XOR operation
|
|
148
|
+
- `set_not(references: List, alias: Optional[str] = None)` - NOT operation
|
|
149
|
+
- `set_imply(lhs, rhs, alias: Optional[str] = None)` - Implication (lhs → rhs)
|
|
150
|
+
- `set_equiv(lhs, rhs, alias: Optional[str] = None)` - Equivalence (lhs ↔ rhs)
|
|
151
|
+
|
|
152
|
+
### Cardinality Constraints
|
|
153
|
+
- `set_atleast(references: List, value: int, alias: Optional[str] = None)` - At least N must be true
|
|
154
|
+
- `set_atmost(references: List, value: int, alias: Optional[str] = None)` - At most N must be true
|
|
155
|
+
- `set_equal(references: List, value: Union[int, str], alias: Optional[str] = None)` - Exactly N must be true
|
|
156
|
+
|
|
157
|
+
### Linear Constraints
|
|
158
|
+
- `set_gelineq(coefficients: Dict, bias: int, alias: Optional[str] = None)` - Greater-or-equal linear inequality (ax + b >= 0)
|
|
159
|
+
|
|
160
|
+
### DAG Operations
|
|
161
|
+
- `sub(root)` - Extract sub-DAG from a root node
|
|
162
|
+
- `sub_many(roots: List)` - Extract multiple sub-DAGs
|
|
163
|
+
- `get_node_ids(dag)` - Get all node IDs in a DAG
|
|
164
|
+
- `get_ids_from_dag(dag)` - Get all node IDs from a DAG (alternative)
|
|
165
|
+
- `validate(dag)` - Validate DAG structure
|
|
166
|
+
- `ranks(dag)` - Compute topological ranks
|
|
167
|
+
|
|
168
|
+
### Alias Operations
|
|
169
|
+
- `get_id_from_alias(alias: str)` - Get node ID from alias
|
|
170
|
+
- `get_alias(id)` - Get alias for a node ID
|
|
171
|
+
- `get_aliases_from_id(id)` - Get all aliases for a node ID
|
|
172
|
+
- `get_ids_from_aliases(aliases: List[str])` - Get IDs for multiple aliases
|
|
173
|
+
|
|
174
|
+
### Node Operations
|
|
175
|
+
- `get_node(id)` - Get a single node
|
|
176
|
+
- `get_nodes(ids: List)` - Get multiple nodes
|
|
177
|
+
- `get_property_values(property: str)` - Get all nodes with a specific property
|
|
178
|
+
|
|
179
|
+
### Propagation
|
|
180
|
+
- `propagate(assignments: Dict)` - Propagate constraints with assignments
|
|
181
|
+
- `propagate_many(many_assignments: List[Dict])` - Propagate multiple assignment sets
|
|
182
|
+
|
|
183
|
+
### Solver
|
|
184
|
+
- `solve(dag, objective: Dict, assume: Optional[Dict] = None, maximize: bool = True)` - Solve single optimization
|
|
185
|
+
- `solve_many(dag, objectives: List[Dict], assume: Optional[Dict] = None, maximize: bool = True)` - Solve multiple optimizations
|
|
186
|
+
|
|
187
|
+
### Deletion
|
|
188
|
+
- `delete_node(id)` - Delete a single node
|
|
189
|
+
- `delete_sub(roots: List)` - Delete sub-DAGs from roots
|
|
190
|
+
|
|
191
|
+
### Client Methods
|
|
192
|
+
- `OatClient(url: str)` - Initialize client with server URL
|
|
193
|
+
- `client.execute(call: FunctionCall)` - Execute a single function call
|
|
194
|
+
- `client.execute_many(calls: List[FunctionCall])` - Execute multiple function calls
|
|
195
|
+
|
|
196
|
+
## Complete Example
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from oatdb import (
|
|
200
|
+
OatClient, set_primitive, set_property, set_and, set_or,
|
|
201
|
+
set_imply, set_atleast, set_gelineq, sub, solve
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Initialize
|
|
205
|
+
client = OatClient("http://localhost:7061")
|
|
206
|
+
|
|
207
|
+
# Create primitives
|
|
208
|
+
x = set_primitive("x", bound=10j)
|
|
209
|
+
y = set_primitive("y", bound=10j)
|
|
210
|
+
z = set_primitive("z", bound=10j)
|
|
211
|
+
|
|
212
|
+
# Add metadata
|
|
213
|
+
x_type = set_property(x, "type", "variable")
|
|
214
|
+
x_priority = set_property(x, "priority", 10)
|
|
215
|
+
|
|
216
|
+
# Create constraints
|
|
217
|
+
and_constraint = set_and([x, y], alias="both_xy")
|
|
218
|
+
or_constraint = set_or([y, z])
|
|
219
|
+
imply_constraint = set_imply(x, y) # x → y
|
|
220
|
+
|
|
221
|
+
# Cardinality: at least 2 must be true
|
|
222
|
+
atleast_2 = set_atleast([x, y, z], 2)
|
|
223
|
+
|
|
224
|
+
# Linear constraint: 2x + 3y - z + 5 >= 0
|
|
225
|
+
linear = set_gelineq(
|
|
226
|
+
coefficients={x: 2, y: 3, z: -1},
|
|
227
|
+
bias=5
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Combine all constraints
|
|
231
|
+
root = set_and([atleast_2, linear], alias="root")
|
|
232
|
+
|
|
233
|
+
# Extract DAG
|
|
234
|
+
dag = sub(root)
|
|
235
|
+
|
|
236
|
+
# Solve optimization: maximize 3x + 2y + z
|
|
237
|
+
solution = solve(
|
|
238
|
+
dag=dag,
|
|
239
|
+
objective={
|
|
240
|
+
x: 3,
|
|
241
|
+
y: 2,
|
|
242
|
+
z: 1
|
|
243
|
+
},
|
|
244
|
+
assume={
|
|
245
|
+
root: 1+1j
|
|
246
|
+
},
|
|
247
|
+
maximize=True
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Execute and get results
|
|
251
|
+
result = client.execute(solution)
|
|
252
|
+
|
|
253
|
+
print("Solution:")
|
|
254
|
+
for var, bounds in result.items():
|
|
255
|
+
if isinstance(bounds, complex):
|
|
256
|
+
print(f" {var}: [{bounds.real}, {bounds.imag}]")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Working with Aliases
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from oatdb import OatClient, set_primitive, set_and, get_id_from_alias, get_alias
|
|
263
|
+
|
|
264
|
+
client = OatClient("http://localhost:7061")
|
|
265
|
+
|
|
266
|
+
# Create constraint with alias
|
|
267
|
+
x = set_primitive("x", bound=1j)
|
|
268
|
+
y = set_primitive("y", bound=1j)
|
|
269
|
+
constraint = set_and([x, y], alias="my_constraint")
|
|
270
|
+
|
|
271
|
+
# Execute creation
|
|
272
|
+
client.execute_many([x, y, constraint])
|
|
273
|
+
|
|
274
|
+
# Query by alias
|
|
275
|
+
id_from_alias = get_id_from_alias("my_constraint")
|
|
276
|
+
alias_from_id = get_alias(id_from_alias)
|
|
277
|
+
|
|
278
|
+
result = client.execute_many([id_from_alias, alias_from_id])
|
|
279
|
+
print(f"ID: {result[id_from_alias.out]}")
|
|
280
|
+
print(f"Alias: {result[alias_from_id.out]}")
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Propagation Example
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from oatdb import OatClient, set_primitive, set_and, propagate
|
|
287
|
+
|
|
288
|
+
client = OatClient("http://localhost:7061")
|
|
289
|
+
|
|
290
|
+
# Create AND constraint
|
|
291
|
+
a = set_primitive("a", bound=1j)
|
|
292
|
+
b = set_primitive("b", bound=1j)
|
|
293
|
+
c = set_primitive("c", bound=1j)
|
|
294
|
+
and_gate = set_and([a, b, c], alias="and_gate")
|
|
295
|
+
|
|
296
|
+
# Propagate: if AND is true, what can we infer?
|
|
297
|
+
prop_result = propagate(
|
|
298
|
+
assignments={
|
|
299
|
+
a: 1+1j,
|
|
300
|
+
b: 1+1j,
|
|
301
|
+
c: 1+1j,
|
|
302
|
+
and_gate: 1j # Upper bound only
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
result = client.execute(prop_result)
|
|
307
|
+
# Result will show that a, b, and c must all be [1, 1]
|
|
308
|
+
print(f"Inferred bounds: {result}")
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Testing
|
|
312
|
+
|
|
313
|
+
### Run the comprehensive test suite:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
cd clients/Python
|
|
317
|
+
python tests/test_client.py
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Or with Poetry:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
poetry run python tests/test_client.py
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Note**: Make sure the OatDB server is running on `http://localhost:7061` before running tests.
|
|
327
|
+
|
|
328
|
+
### Run examples:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
python examples.py
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Requirements
|
|
335
|
+
|
|
336
|
+
- Python >= 3.10
|
|
337
|
+
- requests >= 2.31.0
|
|
338
|
+
|
|
339
|
+
## Development
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Install with Poetry
|
|
343
|
+
poetry install
|
|
344
|
+
|
|
345
|
+
# Run tests
|
|
346
|
+
poetry run pytest
|
|
347
|
+
|
|
348
|
+
# Format code
|
|
349
|
+
poetry run black .
|
|
350
|
+
|
|
351
|
+
# Type checking
|
|
352
|
+
poetry run mypy oatdb
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
MIT
|
|
358
|
+
|
|
359
|
+
## Links
|
|
360
|
+
|
|
361
|
+
- [GitHub Repository](https://github.com/yourusername/oat-db-rust-v2)
|
|
362
|
+
- [OatDB Documentation](https://github.com/yourusername/oat-db-rust-v2)
|
|
363
|
+
- [Rust Client](https://github.com/yourusername/oat-db-rust-v2/tree/main/clients/Rust)
|
|
364
|
+
|
oatdb-0.4.0/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# OatDB Python SDK
|
|
2
|
+
|
|
3
|
+
A Python client library for interacting with the OatDB (Optimization and Analysis Tooling) database backend.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Full support for all 30 OatDB API functions
|
|
8
|
+
- ✅ Logical operations (AND, OR, XOR, NOT, IMPLY, EQUIV)
|
|
9
|
+
- ✅ Cardinality constraints (AtLeast, AtMost, Equal)
|
|
10
|
+
- ✅ Linear inequality constraints (GeLineq)
|
|
11
|
+
- ✅ Property management
|
|
12
|
+
- ✅ DAG operations (sub, sub_many, validate, ranks)
|
|
13
|
+
- ✅ Constraint propagation
|
|
14
|
+
- ✅ Optimization solver (solve, solve_many)
|
|
15
|
+
- ✅ Node deletion and management
|
|
16
|
+
- ✅ Alias support for named constraints
|
|
17
|
+
- ✅ Type hints for better IDE support
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install oat-python-sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or with Poetry:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
poetry add oat-python-sdk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from oatdb import OatClient, set_primitive, set_property, set_and, sub, solve
|
|
35
|
+
|
|
36
|
+
# Initialize client
|
|
37
|
+
client = OatClient("http://localhost:7061")
|
|
38
|
+
|
|
39
|
+
# Create primitives with bounds [min, max]
|
|
40
|
+
x = set_primitive("x", bound=1j) # [0, 1]
|
|
41
|
+
y = set_primitive("y", bound=10j) # [0, 10]
|
|
42
|
+
|
|
43
|
+
# Add properties
|
|
44
|
+
x_name = set_property(x, "name", "Variable X")
|
|
45
|
+
|
|
46
|
+
# Create constraints
|
|
47
|
+
constraint = set_and([x, y], alias="my_constraint")
|
|
48
|
+
|
|
49
|
+
# Extract DAG and solve
|
|
50
|
+
dag = sub(constraint)
|
|
51
|
+
solution = solve(
|
|
52
|
+
dag=dag,
|
|
53
|
+
objective={
|
|
54
|
+
x: 1,
|
|
55
|
+
y: 2
|
|
56
|
+
},
|
|
57
|
+
assume={
|
|
58
|
+
# Force constraint to be true
|
|
59
|
+
constraint: 1+1j
|
|
60
|
+
},
|
|
61
|
+
maximize=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Execute and get results
|
|
65
|
+
result = client.execute(solution)
|
|
66
|
+
print(result)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Core Concepts
|
|
70
|
+
|
|
71
|
+
### Bounds
|
|
72
|
+
|
|
73
|
+
Bounds are represented as complex numbers where:
|
|
74
|
+
- Real part = lower bound
|
|
75
|
+
- Imaginary part = upper bound
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Bound [0, 1]
|
|
79
|
+
bound = 1j
|
|
80
|
+
|
|
81
|
+
# Bound [5, 10]
|
|
82
|
+
bound = 5 + 10j
|
|
83
|
+
|
|
84
|
+
# Access bounds from solution
|
|
85
|
+
solution_data = result[solution.out]
|
|
86
|
+
x_bounds = solution_data["x"] # [lower, upper] as list
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Function Calls and Execution
|
|
90
|
+
|
|
91
|
+
All operations return `FunctionCall` objects that you execute using the client:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from oatdb import OatClient, set_primitive, set_and, sub
|
|
95
|
+
|
|
96
|
+
client = OatClient("http://localhost:7061")
|
|
97
|
+
|
|
98
|
+
# Create function calls
|
|
99
|
+
x = set_primitive("x", bound=1j)
|
|
100
|
+
y = set_primitive("y", bound=1j)
|
|
101
|
+
constraint = set_and([x, y])
|
|
102
|
+
|
|
103
|
+
# Execute a single operation
|
|
104
|
+
result = client.execute(constraint)
|
|
105
|
+
|
|
106
|
+
# Execute multiple operations
|
|
107
|
+
dag = sub(constraint)
|
|
108
|
+
result = client.execute_many([x, y, constraint, dag])
|
|
109
|
+
|
|
110
|
+
# Access results by the function call's output key
|
|
111
|
+
dag_data = result[dag.out]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Available Functions
|
|
115
|
+
|
|
116
|
+
All functions return `FunctionCall` objects that are executed using `client.execute()` or `client.execute_many()`.
|
|
117
|
+
|
|
118
|
+
### Primitive Operations
|
|
119
|
+
- `set_primitive(id: str, bound: complex = 1j, alias: Optional[str] = None)` - Create a single primitive
|
|
120
|
+
- `set_primitives(ids: List[str], bound: complex = 1j)` - Create multiple primitives
|
|
121
|
+
- `set_property(id: Union[str, FunctionCall], property: str, value: Any)` - Set node property
|
|
122
|
+
|
|
123
|
+
### Logical Operations
|
|
124
|
+
- `set_and(references: List, alias: Optional[str] = None)` - AND operation
|
|
125
|
+
- `set_or(references: List, alias: Optional[str] = None)` - OR operation
|
|
126
|
+
- `set_xor(references: List, alias: Optional[str] = None)` - XOR operation
|
|
127
|
+
- `set_not(references: List, alias: Optional[str] = None)` - NOT operation
|
|
128
|
+
- `set_imply(lhs, rhs, alias: Optional[str] = None)` - Implication (lhs → rhs)
|
|
129
|
+
- `set_equiv(lhs, rhs, alias: Optional[str] = None)` - Equivalence (lhs ↔ rhs)
|
|
130
|
+
|
|
131
|
+
### Cardinality Constraints
|
|
132
|
+
- `set_atleast(references: List, value: int, alias: Optional[str] = None)` - At least N must be true
|
|
133
|
+
- `set_atmost(references: List, value: int, alias: Optional[str] = None)` - At most N must be true
|
|
134
|
+
- `set_equal(references: List, value: Union[int, str], alias: Optional[str] = None)` - Exactly N must be true
|
|
135
|
+
|
|
136
|
+
### Linear Constraints
|
|
137
|
+
- `set_gelineq(coefficients: Dict, bias: int, alias: Optional[str] = None)` - Greater-or-equal linear inequality (ax + b >= 0)
|
|
138
|
+
|
|
139
|
+
### DAG Operations
|
|
140
|
+
- `sub(root)` - Extract sub-DAG from a root node
|
|
141
|
+
- `sub_many(roots: List)` - Extract multiple sub-DAGs
|
|
142
|
+
- `get_node_ids(dag)` - Get all node IDs in a DAG
|
|
143
|
+
- `get_ids_from_dag(dag)` - Get all node IDs from a DAG (alternative)
|
|
144
|
+
- `validate(dag)` - Validate DAG structure
|
|
145
|
+
- `ranks(dag)` - Compute topological ranks
|
|
146
|
+
|
|
147
|
+
### Alias Operations
|
|
148
|
+
- `get_id_from_alias(alias: str)` - Get node ID from alias
|
|
149
|
+
- `get_alias(id)` - Get alias for a node ID
|
|
150
|
+
- `get_aliases_from_id(id)` - Get all aliases for a node ID
|
|
151
|
+
- `get_ids_from_aliases(aliases: List[str])` - Get IDs for multiple aliases
|
|
152
|
+
|
|
153
|
+
### Node Operations
|
|
154
|
+
- `get_node(id)` - Get a single node
|
|
155
|
+
- `get_nodes(ids: List)` - Get multiple nodes
|
|
156
|
+
- `get_property_values(property: str)` - Get all nodes with a specific property
|
|
157
|
+
|
|
158
|
+
### Propagation
|
|
159
|
+
- `propagate(assignments: Dict)` - Propagate constraints with assignments
|
|
160
|
+
- `propagate_many(many_assignments: List[Dict])` - Propagate multiple assignment sets
|
|
161
|
+
|
|
162
|
+
### Solver
|
|
163
|
+
- `solve(dag, objective: Dict, assume: Optional[Dict] = None, maximize: bool = True)` - Solve single optimization
|
|
164
|
+
- `solve_many(dag, objectives: List[Dict], assume: Optional[Dict] = None, maximize: bool = True)` - Solve multiple optimizations
|
|
165
|
+
|
|
166
|
+
### Deletion
|
|
167
|
+
- `delete_node(id)` - Delete a single node
|
|
168
|
+
- `delete_sub(roots: List)` - Delete sub-DAGs from roots
|
|
169
|
+
|
|
170
|
+
### Client Methods
|
|
171
|
+
- `OatClient(url: str)` - Initialize client with server URL
|
|
172
|
+
- `client.execute(call: FunctionCall)` - Execute a single function call
|
|
173
|
+
- `client.execute_many(calls: List[FunctionCall])` - Execute multiple function calls
|
|
174
|
+
|
|
175
|
+
## Complete Example
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from oatdb import (
|
|
179
|
+
OatClient, set_primitive, set_property, set_and, set_or,
|
|
180
|
+
set_imply, set_atleast, set_gelineq, sub, solve
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Initialize
|
|
184
|
+
client = OatClient("http://localhost:7061")
|
|
185
|
+
|
|
186
|
+
# Create primitives
|
|
187
|
+
x = set_primitive("x", bound=10j)
|
|
188
|
+
y = set_primitive("y", bound=10j)
|
|
189
|
+
z = set_primitive("z", bound=10j)
|
|
190
|
+
|
|
191
|
+
# Add metadata
|
|
192
|
+
x_type = set_property(x, "type", "variable")
|
|
193
|
+
x_priority = set_property(x, "priority", 10)
|
|
194
|
+
|
|
195
|
+
# Create constraints
|
|
196
|
+
and_constraint = set_and([x, y], alias="both_xy")
|
|
197
|
+
or_constraint = set_or([y, z])
|
|
198
|
+
imply_constraint = set_imply(x, y) # x → y
|
|
199
|
+
|
|
200
|
+
# Cardinality: at least 2 must be true
|
|
201
|
+
atleast_2 = set_atleast([x, y, z], 2)
|
|
202
|
+
|
|
203
|
+
# Linear constraint: 2x + 3y - z + 5 >= 0
|
|
204
|
+
linear = set_gelineq(
|
|
205
|
+
coefficients={x: 2, y: 3, z: -1},
|
|
206
|
+
bias=5
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Combine all constraints
|
|
210
|
+
root = set_and([atleast_2, linear], alias="root")
|
|
211
|
+
|
|
212
|
+
# Extract DAG
|
|
213
|
+
dag = sub(root)
|
|
214
|
+
|
|
215
|
+
# Solve optimization: maximize 3x + 2y + z
|
|
216
|
+
solution = solve(
|
|
217
|
+
dag=dag,
|
|
218
|
+
objective={
|
|
219
|
+
x: 3,
|
|
220
|
+
y: 2,
|
|
221
|
+
z: 1
|
|
222
|
+
},
|
|
223
|
+
assume={
|
|
224
|
+
root: 1+1j
|
|
225
|
+
},
|
|
226
|
+
maximize=True
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Execute and get results
|
|
230
|
+
result = client.execute(solution)
|
|
231
|
+
|
|
232
|
+
print("Solution:")
|
|
233
|
+
for var, bounds in result.items():
|
|
234
|
+
if isinstance(bounds, complex):
|
|
235
|
+
print(f" {var}: [{bounds.real}, {bounds.imag}]")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Working with Aliases
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from oatdb import OatClient, set_primitive, set_and, get_id_from_alias, get_alias
|
|
242
|
+
|
|
243
|
+
client = OatClient("http://localhost:7061")
|
|
244
|
+
|
|
245
|
+
# Create constraint with alias
|
|
246
|
+
x = set_primitive("x", bound=1j)
|
|
247
|
+
y = set_primitive("y", bound=1j)
|
|
248
|
+
constraint = set_and([x, y], alias="my_constraint")
|
|
249
|
+
|
|
250
|
+
# Execute creation
|
|
251
|
+
client.execute_many([x, y, constraint])
|
|
252
|
+
|
|
253
|
+
# Query by alias
|
|
254
|
+
id_from_alias = get_id_from_alias("my_constraint")
|
|
255
|
+
alias_from_id = get_alias(id_from_alias)
|
|
256
|
+
|
|
257
|
+
result = client.execute_many([id_from_alias, alias_from_id])
|
|
258
|
+
print(f"ID: {result[id_from_alias.out]}")
|
|
259
|
+
print(f"Alias: {result[alias_from_id.out]}")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Propagation Example
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from oatdb import OatClient, set_primitive, set_and, propagate
|
|
266
|
+
|
|
267
|
+
client = OatClient("http://localhost:7061")
|
|
268
|
+
|
|
269
|
+
# Create AND constraint
|
|
270
|
+
a = set_primitive("a", bound=1j)
|
|
271
|
+
b = set_primitive("b", bound=1j)
|
|
272
|
+
c = set_primitive("c", bound=1j)
|
|
273
|
+
and_gate = set_and([a, b, c], alias="and_gate")
|
|
274
|
+
|
|
275
|
+
# Propagate: if AND is true, what can we infer?
|
|
276
|
+
prop_result = propagate(
|
|
277
|
+
assignments={
|
|
278
|
+
a: 1+1j,
|
|
279
|
+
b: 1+1j,
|
|
280
|
+
c: 1+1j,
|
|
281
|
+
and_gate: 1j # Upper bound only
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
result = client.execute(prop_result)
|
|
286
|
+
# Result will show that a, b, and c must all be [1, 1]
|
|
287
|
+
print(f"Inferred bounds: {result}")
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Testing
|
|
291
|
+
|
|
292
|
+
### Run the comprehensive test suite:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
cd clients/Python
|
|
296
|
+
python tests/test_client.py
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Or with Poetry:
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
poetry run python tests/test_client.py
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Note**: Make sure the OatDB server is running on `http://localhost:7061` before running tests.
|
|
306
|
+
|
|
307
|
+
### Run examples:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
python examples.py
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Requirements
|
|
314
|
+
|
|
315
|
+
- Python >= 3.10
|
|
316
|
+
- requests >= 2.31.0
|
|
317
|
+
|
|
318
|
+
## Development
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
# Install with Poetry
|
|
322
|
+
poetry install
|
|
323
|
+
|
|
324
|
+
# Run tests
|
|
325
|
+
poetry run pytest
|
|
326
|
+
|
|
327
|
+
# Format code
|
|
328
|
+
poetry run black .
|
|
329
|
+
|
|
330
|
+
# Type checking
|
|
331
|
+
poetry run mypy oatdb
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
|
337
|
+
|
|
338
|
+
## Links
|
|
339
|
+
|
|
340
|
+
- [GitHub Repository](https://github.com/yourusername/oat-db-rust-v2)
|
|
341
|
+
- [OatDB Documentation](https://github.com/yourusername/oat-db-rust-v2)
|
|
342
|
+
- [Rust Client](https://github.com/yourusername/oat-db-rust-v2/tree/main/clients/Rust)
|