Skip to main content

Python client for OAT (Optimization and Analysis Tooling) database

Project description

OatDB Python SDK

A Python client library for interacting with the OatDB (Optimization and Analysis Tooling) database backend.

Features

  • ✅ Full support for all 30 OatDB API functions
  • ✅ Logical operations (AND, OR, XOR, NOT, IMPLY, EQUIV)
  • ✅ Cardinality constraints (AtLeast, AtMost, Equal)
  • ✅ Linear inequality constraints (GeLineq)
  • ✅ Property management
  • ✅ DAG operations (sub, sub_many, validate, ranks)
  • ✅ Constraint propagation
  • ✅ Optimization solver (solve, solve_many)
  • ✅ Node deletion and management
  • ✅ Alias support for named constraints
  • ✅ Type hints for better IDE support

Installation

pip install oat-python-sdk

Or with Poetry:

poetry add oat-python-sdk

Quick Start

from oatdb import OatClient

# Initialize client
client = OatClient("http://localhost:7061")

# Create primitives with bounds [min, max]
x = client.set_primitive("x", bound=1j)  # [0, 1]
y = client.set_primitive("y", bound=10j)  # [0, 10]

# Add properties
client.set_property(x, "name", "Variable X")

# Create constraints
constraint = client.set_and([x, y], alias="my_constraint")

# Extract DAG and solve
dag = client.sub(constraint)
solution = client.solve(
    dag=dag,
    objective=[
        {"id": "x", "coefficient": 1},
        {"id": "y", "coefficient": 2}
    ],
    assume=[
        # Force contraint to be true
        {"id": constraint, "bound": [1,1]}
    ],
    maximize=True
)

# Execute and get results
result = client.execute([solution.out])
print(result)

Core Concepts

Bounds

Bounds are represented as complex numbers where:

  • Real part = lower bound
  • Imaginary part = upper bound
# Bound [0, 1]
bound = 1j

# Bound [5, 10]
bound = 5 + 10j

# Access bounds from solution
solution_data = result[solution.out]
x_bounds = solution_data["x"]  # [lower, upper] as list

Function Calls and Buffering

All operations return FunctionCall objects that are buffered until you call execute():

# Buffer operations
x = client.set_primitive("x", bound=1j)
y = client.set_primitive("y", bound=1j)
constraint = client.set_and([x, y])

# Execute all buffered operations
result = client.execute()

# Or execute and get specific outputs
dag = client.sub(constraint)
result = client.execute([dag.out])

Available Methods

Primitive Operations

  • set_primitive(id: str, bound: complex = 1j, alias: Optional[str] = None) - Create a single primitive
  • set_primitives(ids: List[str], bound: complex = 1j) - Create multiple primitives
  • set_property(id: Union[str, FunctionCall], property: str, value: Any) - Set node property

Logical Operations

  • set_and(references: List, alias: Optional[str] = None) - AND operation
  • set_or(references: List, alias: Optional[str] = None) - OR operation
  • set_xor(references: List, alias: Optional[str] = None) - XOR operation
  • set_not(references: List, alias: Optional[str] = None) - NOT operation
  • set_imply(lhs, rhs, alias: Optional[str] = None) - Implication (lhs → rhs)
  • set_equiv(lhs, rhs, alias: Optional[str] = None) - Equivalence (lhs ↔ rhs)

Cardinality Constraints

  • set_atleast(references: List, value: int, alias: Optional[str] = None) - At least N must be true
  • set_atmost(references: List, value: int, alias: Optional[str] = None) - At most N must be true
  • set_equal(references: List, value: Union[int, str], alias: Optional[str] = None) - Exactly N must be true

Linear Constraints

  • set_gelineq(coefficients: Dict[str, int], bias: int, alias: Optional[str] = None) - Greater-or-equal linear inequality (ax + b >= 0)

DAG Operations

  • sub(root) - Extract sub-DAG from a root node
  • sub_many(roots: List) - Extract multiple sub-DAGs
  • get_node_ids(dag) - Get all node IDs in a DAG
  • validate(dag) - Validate DAG structure
  • ranks(dag) - Compute topological ranks

Alias Operations

  • get_id_from_alias(alias: str) - Get node ID from alias
  • get_alias(id) - Get alias for a node ID
  • get_aliases_from_id(id) - Get all aliases for a node ID
  • get_ids_from_aliases(aliases: List[str]) - Get IDs for multiple aliases

Node Operations

  • get_node(id) - Get a single node
  • get_nodes(ids: List) - Get multiple nodes
  • get_property_values(property: str) - Get all nodes with a specific property

Propagation

  • propagate(assignments: List[Dict]) - Propagate constraints with assignments
  • propagate_many(many_assignments: List[List[Dict]]) - Propagate multiple assignment sets

Solver

  • solve(dag, objective: List[Dict], assume: Optional[List[Dict]] = None, maximize: bool = True) - Solve single optimization
  • solve_many(dag, objectives: List[List[Dict]], assume: Optional[List[Dict]] = None, maximize: bool = True) - Solve multiple optimizations

Deletion

  • delete_node(id) - Delete a single node
  • delete_sub(roots: List) - Delete sub-DAGs from roots

Utility

  • health_check() -> bool - Check server health
  • execute(outputs: Optional[List[str]] = None, clear_buffer: bool = True) - Execute buffered operations
  • clear_buffer() - Clear the operation buffer
  • get_buffer_size() -> int - Get number of buffered operations
  • debug_payload() -> dict - Get the JSON payload that would be sent

Complete Example

from oatdb import OatClient

# Initialize
client = OatClient("http://localhost:7061")

# Create primitives
x = client.set_primitive("x", bound=10j)
y = client.set_primitive("y", bound=10j)
z = client.set_primitive("z", bound=10j)

# Add metadata
client.set_property(x, "type", "variable")
client.set_property(x, "priority", 10)

# Create constraints
and_constraint = client.set_and([x, y], alias="both_xy")
or_constraint = client.set_or([y, z])
imply_constraint = client.set_imply(x, y)  # x → y

# Cardinality: at least 2 must be true
atleast_2 = client.set_atleast([x, y, z], 2)

# Linear constraint: 2x + 3y - z + 5 >= 0
linear = client.set_gelineq(
    coefficients={"x": 2, "y": 3, "z": -1},
    bias=5
)

# Combine all constraints
root = client.set_and([atleast_2, linear], alias="root")

# Extract DAG
dag = client.sub(root)

# Solve optimization: maximize 3x + 2y + z
solution = client.solve(
    dag=dag,
    objective=[
        {"id": "x", "coefficient": 3},
        {"id": "y", "coefficient": 2},
        {"id": "z", "coefficient": 1}
    ],
    assume=[
        {"id": root, "bound": [1,1]}
    ],
    maximize=True
)

# Execute and get results
result = client.execute([solution.out])

print("Solution:")
for var, bounds in result[solution.out].items():
    if isinstance(bounds, list) and len(bounds) == 2:
        print(f"  {var}: [{bounds[0]}, {bounds[1]}]")

Working with Aliases

from oatdb import OatClient

client = OatClient("http://localhost:7061")

# Create constraint with alias
x = client.set_primitive("x", bound=1j)
y = client.set_primitive("y", bound=1j)
constraint = client.set_and([x, y], alias="my_constraint")

# Execute creation
client.execute()

# Query by alias
id_from_alias = client.get_id_from_alias("my_constraint")
alias_from_id = client.get_alias(id_from_alias)

result = client.execute([id_from_alias.out, alias_from_id.out])
print(f"ID: {result[id_from_alias.out]}")
print(f"Alias: {result[alias_from_id.out]}")

Propagation Example

from oatdb import OatClient

client = OatClient("http://localhost:7061")

# Create AND constraint
x = client.set_primitive("x", bound=1j)
y = client.set_primitive("y", bound=1j)
and_gate = client.set_and([x, y], alias="and_gate")

# Execute creation
client.execute()

# Propagate: if AND is true, what can we infer?
prop_result = client.propagate(
    assignments=[
        {"id": "and_gate", "bound": [1, 1]}
    ]
)

result = client.execute([prop_result.out])
# Result will show that x and y must both be [1, 1]
print(result[prop_result.out])

Testing

Run the comprehensive test suite:

cd clients/Python
python tests/test_client.py

Or with Poetry:

poetry run python tests/test_client.py

Note: Make sure the OatDB server is running on http://localhost:7061 before running tests.

Run examples:

python examples.py

Requirements

  • Python >= 3.10
  • requests >= 2.31.0

Development

# Install with Poetry
poetry install

# Run tests
poetry run pytest

# Format code
poetry run black .

# Type checking
poetry run mypy oatdb

License

MIT

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oat_python_sdk-0.3.1.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oat_python_sdk-0.3.1-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file oat_python_sdk-0.3.1.tar.gz.

File metadata

  • Download URL: oat_python_sdk-0.3.1.tar.gz
  • Upload date:
  • Size: 11.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.10.15 Darwin/24.6.0

File hashes

Hashes for oat_python_sdk-0.3.1.tar.gz
Algorithm Hash digest
SHA256 df2adfb36c3b2fecd0d6bcc700a0bc6ba1ec35dc09fb8ab4610211b7f591231b
MD5 84b46af45fcdb773e807868ef1d749d6
BLAKE2b-256 bbdd3be70766b29b9653a71ddb7ffd93626b86dd19cd07ff6a4442bc3ed20273

See more details on using hashes here.

File details

Details for the file oat_python_sdk-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: oat_python_sdk-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.10.15 Darwin/24.6.0

File hashes

Hashes for oat_python_sdk-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ef7511ff7107ab3efba2a30eeb6552d5a418fb532b187f05a8a96024a5f1052c
MD5 8ed9a893cc45c491df8f8a2788041b78
BLAKE2b-256 e9d64dad64871a03930b94c3ce2349ccc2c6d91583f37fc520ce566e4ef3b9e7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page