API reference

opvious

class opvious.AbortedOutcome

Bases: object

The solve was cancelled before a solution was found

class opvious.Client(executor: Executor)

Bases: object

Opvious API client

async annotate_specification(specification: LocalSpecification, ignore_codes: Iterable[str] | None = None) LocalSpecification

Validates a specification, annotating it with any issues

Parameters:
  • specification – The specification to validate

  • ignore_codes – Optional list of error codes to ignore when detecting issues. This can be used for example to allow unused definitions (ERR_UNUSED_DEFINITION code).

property authenticated: bool

Returns true if the client is using a non-empty API token

async cancel_solve(uuid: str) None

Cancels a running solve

This method will throw if the solve does not exist or is not pending anymore.

Parameters:

uuid – The target solve’s ID

classmethod default(endpoint: str, token: str | None = None) Client

Creates a client using the best Executor for the environment

Parameters:
  • endpoint – API endpoint.

  • token – Optional API token.

property executor: Executor

Returns the client’s underlying executor

async export_specification(specification: LocalSpecification, writer: BinaryIO, transformations: list[ProblemTransformation] | None = None) None

Exports a specification to its canonical representation

Parameters:
  • specification – The specification to export

  • transformations – Transformations to apply to the specification

async fetch_solve_inputs(uuid: str) SolveInputs

Retrieves a queued solve’s inputs

Parameters:

uuid – The target queued solve’s UUID

async fetch_solve_outputs(uuid: str) SolveOutputs

Retrieves a successful queued solves’s outputs

This method will throw if the solve did not have a feasible solution.

Parameters:

uuid – The target queued solve’s UUID

async format_problem(problem: Problem, include_line_comments=False) str

Returns the problem’s annotated representation in LP format

Parameters:
  • problemProblem instance to format

  • include_line_comments – Include comment lines in the output. By default these lines are only logged as DEBUG messages.

The LP formatted output will be fully annotated with matching keys and labels:

minimize
  +1 inventory$1 \ [day=0]
  +1 inventory$2 \ [day=1]
  +1 inventory$3 \ [day=2]
  \ ...
subject to
 inventoryPropagation$1: \ [day=1]
  +1 inventory$1 \ [day=1]
  -1 inventory$2 \ [day=0]
  -1 production$1 \ [day=1]
  = -29
 inventoryPropagation$2: \ [day=2]
  -1 inventory$1 \ [day=1]
  +1 inventory$3 \ [day=2]
  -1 production$2 \ [day=2]
  = -36
 \ ...
classmethod from_environment(env: Mapping[str, str] | None = None, default_endpoint: str | None = None) Client | None

Creates a client configured via environment variables

Parameters:
  • env – Environment, defaults to os.environ. The following keys are used: OPVIOUS_ENDPOINT (pointing to the Opvious API), OPVIOUS_TOKEN (containing a corresponding authorization token), and OPVIOUS_EXECUTOR (forcing the use of a particular executor.

  • default_endpoint – Endpoint to use if none was specified in the environment.

This method returns nothing if the OPVIOUS_ENDPOINT environment variable is unset or empty and no default endpoint was set.

async paginate_formulation_solves(name: str, annotations: list[str | tuple[str, str]] | None = None, limit: int = 25) AsyncIterator[QueuedSolve]

Lists queued solves for a given formulation

Parameters:
  • name – Formulation name

  • annotations – Optional annotations to filter solves by

  • limit – Maximum number of solves to return

Solves are sorted from most recently started to least.

async paginate_solves(annotations: list[str | tuple[str, str]] | None = None, limit: int = 25) AsyncIterator[QueuedSolve]

Lists recent queued solves

Parameters:
  • annotations – Optional annotations to filter solves by

  • limit – Maximum number of solves to return

Solves are sorted from most recently started to least.

async poll_solve(uuid: str) SolveNotification | SolveOutcome

Polls a solve for its outcome or latest progress notification

Parameters:

uuid – The target queued solve’s UUID

async queue_solve(problem: Problem, annotations: list[str | tuple[str, str]] | None = None) str

Queues a solve for asynchronous processing

Inputs will be validated locally before the request is sent to the API. From then on, the solve will be queued and begin solving start as soon as enough capacity is available.

Parameters:

problemProblem instance to solve

The returned QueuedSolve instance can be used to:

As a convenience, Client.wait_for_solve_outcome() allows polling an solve until until it completes, backing off exponentially between each poll:

# Queue a new Sudoku solve
solve = await client.queue_solve(
    opvious.Problem(
        specification=opvious.FormulationSpecification("sudoku"),
        parameters={"hints": [(0, 0, 3), (1, 1, 5)]},
    )
)

# Wait for the solve to complete
await client.wait_for_solve_outcome(solve, assert_feasible=True)

# Fetch the solution's data
output_data = await client.fetch_solve_outputs(solve)

# Get a parsed variable as a dataframe
decisions = output_data.variable("decisions")

See also Client.solve() for an alternative for solving problems live.

async register_specification(specification: LocalSpecification, formulation_name: str, tag_names: Sequence[str] | None = None) FormulationSpecification

Saves a local specification within a remote formulation

Parameters:
  • specification – The specification to save

  • formulation_name – The name of the formulation to register the specification in

  • tag_names – Optional list of tags to assign to this specification. The first one, if any, will be used in the returned specification.

The returned formulation can be used to queue solves for example.

async serialize_problem(problem: Problem) Any

Returns a serialized representation of the problem

The returned JSON object is a valid SolveCandidate value and can be used to call the REST API directly.

Parameters:

problemProblem instance to serialize

async solve(problem: Problem, assert_feasible=False, prefer_streaming=True) Solution

Solves an optimization problem remotely

Inputs will be validated before being sent to the API for solving.

Parameters:
  • problemProblem instance to solve

  • assert_feasible – Throw if the final outcome was not feasible

  • prefer_streaming – Show real time progress notifications when possible

The returned solution exposes both metadata (status, objective value, etc.) and solution data (if the solve was feasible):

solution = await client.solve(
    opvious.Problem(
        specification=opvious.FormulationSpecification(
            "porfolio-selection"
        ),
        parameters={
            "covariance": {
                ("AAPL", "AAPL"): 0.2,
                ("AAPL", "MSFT"): 0.1,
                ("MSFT", "AAPL"): 0.1,
                ("MSFT", "MSFT"): 0.25,
            },
            "expectedReturn": {
                "AAPL": 0.15,
                "MSFT": 0.2,
            },
            "desiredReturn": 0.1,
        },
    ),
)

# Metadata is available on `outcome`
print(f"Objective value: {solution.outcome.objective_value}")

# Variable and constraint data are available via `outputs`
optimal_allocation = solution.outputs.variable("allocation")

See also Client.queue_solve() for an alternative for long-running solves.

async summarize_problem(problem: Problem) ProblemSummary

Returns summary statistics about a problem without solving it

Parameters:

problemProblem instance to summarize

async wait_for_solve_outcome(uuid: str, assert_feasible=False) SolveOutcome

Waits for the solve to complete and returns its outcome

This method emits real-time progress messages as INFO logs.

Parameters:
  • uuid – The target solve’s ID

  • assert_feasible – Throw if the final outcome was not feasible

class opvious.EpsilonConstraint(target: Target, absolute_tolerance: float | None = None, relative_tolerance: float | None = None)

Bases: object

Constraint enforcing proximity to a objective’s optimal value

absolute_tolerance: float | None = None

Cap on the absolute value of the final solution vs optimal

relative_tolerance: float | None = None

Cap on the relative value of the final solution vs optimal

target: Target

Target objective

class opvious.FailedOutcome(status: str, message: str, code: str | None = None, tags: Any | None = None)

Bases: object

The solve failed

code: str | None = None

The underlying error’s error code

message: str

The underlying error’s message

status: str

The underlying error’s status

tags: Any = None

Structured data associated with the failure

class opvious.FeasibleOutcome(optimal: bool, objective_value: float | int | None, relative_gap: float | int | None)

Bases: object

A solution was found

objective_value: float | int | None

The solution’s objective value

optimal: bool

Whether this solution was optimal (within gap thresholds)

relative_gap: float | int | None

The solution’s relative gap (0.1 is 10%)

class opvious.FormulationSpecification(formulation_name: str, tag_name: str | None = None)

Bases: object

A specification from an Optimization Hub formulation

This type of specification allows queueing solves and is recommended for production use as it provides history and reproducibility when combined with tag names.

formulation_name: str

The corresponding formulation’s name

tag_name: str | None = None

The matching tag’s name

If absent, the formulation’s default will be used.

class opvious.InfeasibleOutcome

Bases: object

No feasible solution exists

opvious.Label

alias of str

class opvious.LocalSpecification(sources: Sequence[LocalSpecificationSource], description: str | None = None, annotation: LocalSpecificationAnnotation | None = None)

Bases: object

A local model specification

Instances of this class are integrated with IPython’s rich display capabilities and will automatically render their LaTeX sources when output in notebooks.

Note that this type of specification cannot be used to queue solves.

annotation: LocalSpecificationAnnotation | None = None

API-issued annotation

This field is generated automatically by clients’ annotate_specification() method. When any issues are detected, the specification’s pretty-printed representation will highlight any errors.

description: str | None = None

Optional description

classmethod globs(*likes: str, root: str | None = None) LocalSpecification

Creates a local specification from LaTeX definition files

Parameters:
  • *likes – File names or globs

  • root – Optional root folder used for matching file names and globs. As a convenience this argument can also be an existing file’s name, in which case it will be interpreted as its parent directory (this is typically handy when used with __file__).

classmethod inline(*texts: str) LocalSpecification

Creates a local specification from inline LaTeX definitions

source(title: str | None = None) LocalSpecificationSource

Returns the first source, optionally matching a given title

sources: Sequence[LocalSpecificationSource]

The model’s mathematical source definitions

class opvious.LocalSpecificationIssue(source_index: int, start_offset: int, end_offset: int, message: str, code: str)

Bases: object

An issue detected within a specification

class opvious.LocalSpecificationSource(text: str, title: str = 'untitled')

Bases: object

A document containing one or more model definitions

Models can comprise multiple sources, with each source only containing a subset of the model’s definitions. This provides an easy way to share definitions between models.

text: str

The source’s contents

title: str = 'untitled'

A title used to easily identify the source

class opvious.Problem(specification: Specification, parameters: Mapping[str, TensorArgument] | None = None, dimensions: Mapping[str, DimensionArgument] | None = None, transformations: list[ProblemTransformation] | None = None, strategy: SolveStrategy | None = None, options: SolveOptions | None = None)

Bases: object

An optimization problem instance

dimensions: Mapping[str, DimensionArgument] | None = None

Dimension items, keyed by dimension label

If omitted, these will be automatically inferred from the parameters.

options: SolveOptions | None = None

Optional solve options (gap thresholds, timeout, etc.)

parameters: Mapping[str, TensorArgument] | None = None

Input data, keyed by parameter label

Values may be any value accepted by Tensor.from_argument() and must match the corresponding parameter’s definition.

specification: Specification

Model specification

strategy: SolveStrategy | None = None

Optional multi-objective strategy

This argument is required if the problem has two or more objectives.

transformations: list[ProblemTransformation] | None = None

Optional transformations

class opvious.ProblemOutline(objectives: Mapping[str, ObjectiveOutline], dimensions: Mapping[str, DimensionOutline], parameters: Mapping[str, TensorOutline], variables: Mapping[str, TensorOutline], constraints: Mapping[str, ConstraintOutline])

Bases: object

Model metadata

constraints: Mapping[str, ConstraintOutline]

Constraint metadata, keyed by constraint label

dimensions: Mapping[str, DimensionOutline]

Dimension metadata, keyed by dimension label

objectives: Mapping[str, ObjectiveOutline]

Objective metadata, if applicable

parameters: Mapping[str, TensorOutline]

Parameter metadata, keyed by parameter label

variables: Mapping[str, TensorOutline]

Variable metadata, keyed by variable label

class opvious.ProblemSummary(column_count: int, row_count: int, dimensions: DataFrame, parameters: DataFrame, variables: DataFrame, constraints: DataFrame, objectives: DataFrame)

Bases: object

Reified problem summary statistics

column_count: int

Total number of variable columns

constraints: DataFrame

Constraint summary statistics

dimensions: DataFrame

Dimension summary statistics

objectives: DataFrame

Objective summary statistics

parameters: DataFrame

Parameter summary statistics

row_count: int

Total number of constraint rows

variables: DataFrame

Variable summary statistics

class opvious.QueuedSolve(uuid: str, annotations: list[str | tuple[str, str]], outcome: SolveOutcome | None, enqueued_at: datetime, dequeued_at: datetime | None, completed_at: datetime | None, problem_summary: ProblemSummary | None, options: Any, transformations: Any, strategy: Any)

Bases: object

Queued optimization attempt

Solves are queued via Client.queue_solve(), existing queued solves can be retrieved from their UUID via Client.fetch_solve().

annotations: list[str | tuple[str, str]]

Annotation metadata

completed_at: datetime | None

The time the solve completed

dequeued_at: datetime | None

The time the solve started running

property duration: timedelta | None

The solve’s runtime, if it is complete

enqueued_at: datetime

The time the solve was created

outcome: SolveOutcome | None

Final solve outcome, if available

problem_summary: ProblemSummary | None

Summary information about the solved problem

uuid: str

The solve’s unique identifier

class opvious.RemoteSpecification(url: str)

Bases: object

A model specification from a remote URL

url: str

The specification’s http(s) URL

class opvious.Solution(status: Literal['ABORTED', 'FEASIBLE', 'INFEASIBLE', 'OPTIMAL', 'UNBOUNDED', 'UNKNOWN'], outcome: SolveOutcome, problem_summary: ProblemSummary, outputs: SolveOutputs | None = None)

Bases: object

Solver response

property feasible: bool

Returns true iff the solution’s outcome is feasible

outcome: SolveOutcome

Solution metadata

outputs: SolveOutputs | None = None

Solution data, present iff the solution is feasible

problem_summary: ProblemSummary

Problem summary statistics

status: Literal['ABORTED', 'FEASIBLE', 'INFEASIBLE', 'OPTIMAL', 'UNBOUNDED', 'UNKNOWN']

Status string

class opvious.SolveInputs(problem_outline: ProblemOutline, raw_parameters: list[Any], raw_dimensions: list[Any] | None)

Bases: object

Solve input data

dimension(label: str) Index

Returns the dimension for a given label as a pandas Index

parameter(label: str) Series

Returns the parameter for a given label as a pandas Series

problem_outline: ProblemOutline

Target model metadata

raw_dimensions: list[Any] | None

All dimensions in raw format

raw_parameters: list[Any]

All parameters in raw format

class opvious.SolveNotification(dequeued: bool, relative_gap: float | int | None, lp_iteration_count: int | None, cut_count: int | None)

Bases: object

Solve progress update notification

cut_count: int | None

The latest cut count

dequeued: bool

Whether the solve has already been dequeued

lp_iteration_count: int | None

The latest LP iteration count

relative_gap: float | int | None

The latest relative gap

class opvious.SolveOptions(relative_gap_threshold: float | None = None, absolute_gap_threshold: float | None = None, zero_value_threshold: float | None = None, infinity_value_threshold: float | None = None, free_bound_threshold: float | None = None, timeout_millis: float | None = None)

Bases: object

Solving options

absolute_gap_threshold: float | None = None

Absolute gap threshold below which a solution is considered optimal

See also relative_gap_threshold for a relative variant.

free_bound_threshold: float | None = None

Positive magnitude used to decide whether a bound is free

This value should typically be slightly smaller to the infinity value threshold to allow for small offsets to infinite values. The default is 1e12.

infinity_value_threshold: float | None = None

Positive magnitude used to cap all input values

It is illegal for the reified problem to include coefficients higher or equal to this value so the input needs to be such that they are masked out during reification. The default is 1e13.

relative_gap_threshold: float | None = None

Relative gap threshold below which a solution is considered optimal

For example a value of 0.1 will cause a solution to be optimal when the optimality gap is at most 10%. See also absolute_gap_threshold for a non-relative variant.

timeout_millis: float | None = None

Upper bound on solving time

zero_value_threshold: float | None = None

Positive magnitude below which tensor values are assumed equal to zero

This option is also used on solution results, causing values to be omitted from the solution if their dual value is also absent. It is finally used as threshold for rounding integral variables to the nearest integer. The default is 1e-6.

class opvious.SolveOutputs(problem_outline: ProblemOutline, raw_variables: list[Any], raw_constraints: list[Any])

Bases: object

Successful solve output data

constraint(label: str) DataFrame

Returns constraint results for a given label.

The returned dataframe has two columsn: slack and dual_value.

problem_outline: ProblemOutline

Solved model metadata

raw_constraints: list[Any]

All constraints in raw format

raw_variables: list[Any]

All variables in raw format

variable(label: str) DataFrame

Returns variable results for a given label

The returned dataframe has two columns: value and dual_value.

class opvious.SolveStrategy(target: Target, sense: ~typing.Literal['MAXIMIZE', 'MINIMIZE'] | None = None, epsilon_constraints: list[~opvious.data.solves.EpsilonConstraint] = <factory>)

Bases: object

Multi-objective solving strategy

epsilon_constraints: list[EpsilonConstraint]

All epsilon-constraints to apply

classmethod equally_weighted_sum(sense: Literal['MAXIMIZE', 'MINIMIZE'] | None = None)

Returns a strategy optimizing the sum of all objectives

sense: Literal['MAXIMIZE', 'MINIMIZE'] | None = None

Optimization sense

target: Target

Target objective

class opvious.Tensor(entries: list[Any], default_value: float | str = 0)

Bases: object

An n-dimensional matrix

default_value: float | str = 0

Value to use for missing key

entries: list[Any]

Raw list of matrix entries

classmethod from_argument(arg: TensorArgument, rank: int, is_indicator: bool = False, is_pin: bool = False)

Creates a tensor from a variety of argument values

In most cases you will not need to call this method directly: it is called automatically during parameter validation.

Parameters:
  • arg – Raw argument to be wrapped into a tensor. This argument must match the tensor’s shape, see below for details.

  • rank – The expected rank of the tensor

  • is_indicator – Whether the tensor holds indicator values

The accepted arguments depend on the tensor’s domain rank (the length of its keys) and image (does it hold arbitrary numbers or only 0s/1s). If the tensor’s rank is 0, i.e. is a scalar, the only accepted argument type is a number matching the image. Otherwise the following inputs are accepted:

  • Mapping (e.g. Python dictionary) with tuple key if rank is greater than 1

  • pandas series with key index

Additionally, if the tensor holds indicator values, two more conveniences are allowed, each representing the set of keys which have value 1:

  • Iterable of keys

  • pandas dataframe

Finally, all non-scalar tensor arguments can be wrapped into a tuple (arg, default) to provide a default value to use when no matching key exists.

class opvious.UnboundedOutcome

Bases: object

No bounded optimal solution exists

exception opvious.UnexpectedSolveOutcomeError(outcome: SolveOutcome)

Bases: Exception

The solve ended with an unexpected outcome

opvious.load_notebook_models(path: str, root: str | None = None, allow_empty=False, include_classes=False, include_symbols: Sequence[str] = ()) SimpleNamespace

Loads all models from a notebook

Parameters:
  • path – Path to the notebook, relative to root if present otherwise CWD

  • root – Root path. If set to a file, its parent directory will be used (convenient for use with __file__).

  • allow_empty – Do not throw an error if no exports were found

  • include_classes – Also include Model subclasses

  • include_symbols – Also add values with these names

opvious.solve_outcome_status(outcome: SolveOutcome) Literal['ABORTED', 'FEASIBLE', 'INFEASIBLE', 'OPTIMAL', 'UNBOUNDED', 'UNKNOWN']

Returns the status corresponding to a given outcome

opvious.executors

class opvious.executors.BinaryExecutorResult(status: int, trace: str | None, reader: Any)

Bases: ExecutorResult

Binary execution result

class opvious.executors.Executor(variant: str, endpoint: str, authorization: str | None = None, supports_streaming=False)

Bases: object

Generic HTTP request executor

property authenticated

Whether requests use an API authorization header

property endpoint: str

The executor’s root endpoint, used for all relative URLs

execute(result_type: Type[ExpectedExecutorResult], url: str, method: str = 'GET', headers: dict[str, str] | None = None, json_data: Any | None = None) AsyncIterator[ExpectedExecutorResult]

Send a request

async execute_graphql_query(query: str, variables: Mapping[str, Any] | None = None) Any

Send a GraphQL API request

exception opvious.executors.ExecutorError(status: int | None = None, trace: str | None = None, reason: Any | None = None)

Bases: Exception

Local representation of an error during an executor’s request

class opvious.executors.ExecutorResult(status: int, trace: str | None)

Bases: object

Request execution result

status: int

Response HTTP status code

trace: str | None

Request trace ID

class opvious.executors.JsonExecutorResult(status: int, trace: str | None, text: str)

Bases: ExecutorResult

Unary JSON execution result

class opvious.executors.JsonSeqExecutorResult(status: int, trace: str | None, reader: Any)

Bases: ExecutorResult

Streaming JSON execution result

class opvious.executors.PlainTextExecutorResult(status: int, trace: str | None, reader: Any)

Bases: ExecutorResult

Plain text execution result

opvious.executors.aiohttp_executor(endpoint: str, authorization: str | None = None) Executor

Returns an aiohttp-powered executor

opvious.executors.authorization_header(token: str) str

Generates a suitable authorization header from a token

opvious.executors.default_executor(endpoint: str, authorization: str | None = None) Executor

Infers the best executor for the current environment

async opvious.executors.fetch_csv(url: str, **kwargs) DataFrame

Fetches a CSV from a URL

This convenience method is provided as a portable way to fetch data from notebooks. Using the default pandas.read_csv does not support https:// URLs from JupyterLite (Pyodide).

Parameters:
  • url – The URL to fetch (via GET)

  • **kwargs – Keyword arguments forwarded to pandas.read_csv

async opvious.executors.fetch_text(url: str) str

Fetches a URL’s contents as plain text

Parameters:

url – The URL to fetch (via GET)

opvious.executors.pyodide_executor(endpoint: str, authorization: str | None = None) Executor

Returns a Pyodide-compatible executor

opvious.executors.urllib_executor(endpoint: str, authorization: str | None = None) Executor

Returns a native urllib executor

opvious.modeling

Modeling components

class opvious.modeling.Constraint(body: Callable[[], Quantified[Predicate]], label: Label | None = None, qualifiers: Sequence[Label] | None = None)

Bases: Definition

Optimization constraint

Constraints are best created directly from Model methods via the constraint() decorator.

class ProductModel:
    products = Dimension()
    count = Variable.natural(products)

    @constraint
    def at_least_one(self):
        yield total(self.count(p) for p in self.products) >= 1
class opvious.modeling.Cross(_quantifiers: tuple[Quantifier, ...], _lifted: tuple[Quantifier, ...] | None)

Bases: Sequence[Quantifier]

Cross-product result

class opvious.modeling.Definition

Bases: object

Base model definition

Definition instances embedded within a model are automatically detected and added to its specification. There are several different types of definitions:

class opvious.modeling.Dimension(*, label: str | None = None, name: str | None = None, is_numeric: bool = False)

Bases: Definition, ScalarSpace

An abstract collection of values

Parameters:
  • label – Dimension label override. By default the label is derived from the attribute’s name

  • name – The dimension’s name. By default the name will be derived from the dimension’s label

  • is_numeric – Whether the dimension will only contain integers. This enables arithmetic operations on this dimension’s quantifiers

Dimensions are Quantifiable and as such can be quantified over using cross(). As a convenience, iterating on a dimension also yields a suitable quantifier. This allows creating simple constraints directly:

class ProductModel(Model):
    products = Dimension()
    count = Variable.natural(products)

    @constraint
    def at_least_one_of_each(self):
        for p in self.products:  # Note the iteration here
            yield self.count(p) >= 1
class opvious.modeling.Domain(quantifiers: 'tuple[QuantifierIdentifier, ...]', mask: 'Optional[Predicate]' = None)

Bases: object

class opvious.modeling.Expression

Bases: object

Base expression

Expressions are typically created via Parameter or Variable instances. They can be combined using any of the following operations:

  • Addition: x + y

  • Substraction: x - y

  • Multiplication: x * y

  • Modulo: x % y

  • Integer division: x // y

  • Floating division: x / y

  • Power: x ** y

Literal numbers will automatically be promoted to expressions when used in any of the operations above. They may also be manually wrapped with literal().

Other types of expressions may be created by one of the functions below:

See also Predicate for the list of supported comparison operators.

class opvious.modeling.Image(lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, is_integral: bool = False)

Bases: object

A tensor’s set of possible values

is_integral: bool = False

Whether the image only contain integers

lower_bound: ExpressionLike = -inf

The image’s smallest value (inclusive)

upper_bound: ExpressionLike = inf

The image’s largest value (inclusive)

class opvious.modeling.IterableSpace(*args, **kwargs)

Bases: Protocol[_Q]

Base protocol for spaces which can also be directly iterated on

It is exposed mostly as a typing convenience for typing model fragments. Space is typically used for providing the underlying implementation.

class opvious.modeling.Model(dependencies: Iterable[Model] | None = None, prefix: Sequence[str] | None = None, title: str | None = None)

Bases: object

An optimization model

Parameters:
  • dependencies – Optional list of models upon which this model’s definitions depend. Dependencies’ definitions will be automatically added when generating this model’s specification.

  • prefix – Optional prefix added to all generated labels in this model

  • title – Optional title used when creating the model’s LocalSpecification

definition_counts() DataFrame

Returns a dataframe summarizing the number of definitions

property dependencies: Sequence[Model]

The model’s list of dependencies

property prefix: Sequence[str]

The model’s label prefix, or an empty list if unset

specification(align: bool | None = None) LocalSpecification

Generates the model’s specification

This specification can be used to interact with Client methods, for example to start a solve.

Parameters:

align – Embed generated statement definitions inside an align environment. This can be disabled to enable pretty printing in environments which do not support them (e.g. Colab).

statements() Iterable[Statement]

Lists the model’s (and any dependencies’) statements

property title: str

The model’s title, defaulting to its class’ __qualname__

class opvious.modeling.ModelFragment

Bases: object

Reusable model sub-component

Model fragments are useful to group related definitions together and expose them in a reusable way. See the API reference for the list of available fragments.

property default_definition: str | None

Optional label that will be used as the fragment’s default.

The matching definition’s final label will be shortened to the fragment’s prefix.

class opvious.modeling.Objective(body: Callable[[], Expression], sense: Literal['max', 'min'], label: str | None = None)

Bases: Definition

Optimization objective

Objectives are best created directly from Model methods via the objective() decorator.

class ProductModel:
    products = Dimension()
    count = Variable.natural(products)

    @objective
    def minimize_total_count(self):
        return total(self.count(p) for p in self.products)
class opvious.modeling.Parameter(image: Image, *quantifiables: Quantifiable, name: str | None = None, label: str | None = None, qualifiers: Sequence[str] | None = None)

Bases: Tensor

An optimization input parameter

Parameters:
  • image – Target Image

  • quantifiables – Quantification

  • label – Optional label, if unset it will be derived from the parameter’s attribute name

  • name – Optional name, if unset it will be derived from the labe

  • qualifiers – Optional list of labels used to name this parameter’s quantifiables

Consider instantiating parameters via one of the various Tensor convenience class methods, for example:

p1 = Parameter.continuous()  # Real-valued parameter
p2 = Parameter.natural() # # Parameter with values in {0, 1...}

Each parameter will be expected to have a matching input when solving this model on data.

class opvious.modeling.Predicate

Bases: object

A predicate on expressions

Instances of this class are generated by using comparison operators on Expression instances. The example below shows the three types of predicates supported as constraints:

class ProductModel(Model):
    products = Dimension()
    cost = Parameter(products)
    count = Variable(products)

    @constraint
    def total_cost_is_at_most_100(self):
        total_cost = total(
            self.cost(p) * self.count(p)
            for p in self.products
        )
        yield total_cost <= 100  # LEQ predicate

    @constraint
    def each_count_is_at_least_10(self):
        for p in self.products:
            yield self.count(p) >= 10  # GEQ predicate

    @constraint
    def exactly_10_of_expensive_items(self):
        for p in self.products:
            if self.cost(p) > 50:  # See below
                yield self.count(p) == 10  # EQ predicate

Additional types of predicates may be generated on expressions which do not include variables and used as conditionals within a quantified expression. For example the exactly_10_of_expensive_items constraint above uses a greater than predicate to filter the set of products where the constraint applies.

opvious.modeling.Projection

alias of int

class opvious.modeling.Quantification(_quantifiables: tuple[Quantifiable, ...], _names: Mapping[int, str], _projection: Projection, _lift: bool, __hash__: Any | None = None)

Bases: Space

Cross-product quantification

class opvious.modeling.Quantifier(identifier: QuantifierIdentifier)

Bases: Expression

An expression used to index a quantifiable space

Quantifiers are generated by using cross() and its convenience alternatives (for example iterating on a Dimension). You should not need to instantiate them directly - this class is only exposed for typing purposes.

class opvious.modeling.Space

Bases: object

Base quantification

This class provides support for generating cross-products with the * operator (see cross()):

space1 * space2 # Equivalent to cross(space1, space2)
class opvious.modeling.Statement(title: str, category: Literal['ALIAS', 'CONSTRAINT', 'DIMENSION', 'OBJECTIVE', 'PARAMETER', 'VARIABLE'], label: str, name: str | None, text: str)

Bases: object

A rendered definition

category: Literal['ALIAS', 'CONSTRAINT', 'DIMENSION', 'OBJECTIVE', 'PARAMETER', 'VARIABLE']

The type of definition this statement contains

label: str

The definition’s label

name: str | None

The definition’s name, if applicable

text: str

The definition’s mathematical representation

title: str

The title of the model the definition belongs to

class opvious.modeling.Tensor(image: Image, *quantifiables: Quantifiable, name: str | None = None, label: str | None = None, qualifiers: Sequence[str] | None = None)

Bases: Definition

Base tensor class

Calling a tensor returns an Expression with any arguments as subscripts. For example:

class ProductModel(Model):
    products = Dimension()
    count = Variable.natural(products)

    @property
    def total_product_count(self):
        return total(
            self.count(p)  # Expression representing the count for `p`
            for p in self.products
        )

The number of arguments must match the tensor’s quantification.

classmethod continuous(*quantifiables, lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, **kwargs) _T

Returns a tensor with real image

classmethod discrete(*quantifiables, lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, **kwargs) _T

Returns a tensor with integral image

property image: Image

The tensor’s values’ image

classmethod indicator(*quantifiables, **kwargs) _T

Returns a tensor with {0, 1} integral image

classmethod natural(*quantifiables, upper_bound: ExpressionLike = inf, **kwargs) _T

Returns a tensor with non-negative integral image

classmethod non_negative(*quantifiables, upper_bound: ExpressionLike = inf, **kwargs) _T

Returns a tensor with non-negative real image

classmethod non_positive(*quantifiables, lower_bound: ExpressionLike = -inf, **kwargs) _T

Returns a tensor with non-positive real image

quantifiables() tuple[Quantifiable, ...]

The quantifiables generating the tensor’s space

This is typically useful to support projections in model fragments. For most use cases consider using the simpler space() method.

space() IterableSpace[Sequence[Quantifier]]

Returns the tensor’s underlying space

total(absolute=False) Expression

The tensor’s total summed value

Parameters:

absolute – Sum the absolute value of the tensor’s terms instead of its raw values

classmethod unit(*quantifiables, **kwargs) _T

Returns a tensor with [0, 1] real image

class opvious.modeling.Variable(image: Image, *quantifiables: Quantifiable, name: str | None = None, label: str | None = None, qualifiers: Sequence[str] | None = None)

Bases: Tensor

An optimization output variable

Parameters:
  • image – Target Image

  • quantifiables – Quantification

  • label – Optional label, if unset it will be derived from the parameter’s attribute name

  • name – Optional name, if unset it will be derived from the labe

  • qualifiers – Optional list of labels used to name this parameter’s quantifiables

Similar to parameters, consider instantiating variables via one of the various Tensor convenience class methods, for example:

v1 = Variable.unit()  # Variable with value within [0, 1]
v2 = Variable.non_negative() # # Variable with value at least 0

Each variable will have its results available in feasible solve solutions.

opvious.modeling.cross(*quantifiables: Quantifiable, names: Iterable[str] | None = None, projection: Projection = -1, lift=False) Quantification

Generates the cross-product of multiple quantifiables

Parameters:
  • quantifiables – One or more quantifiables

  • names – Optional names for the generated quantifiers

  • projection – Quantifiable selection mask

  • lift – Returns lifted Cross instances. Setting this option will include all masks present in the original quantifiable, even if they are not projected.

This function is the core building block for quantifying values.

opvious.modeling.domain(quantifiable: Quantifiable, names: Iterable[str] | None = None) Domain

Creates a domain from a quantifiable

opvious.modeling.interval(lower_bound: ExpressionLike, upper_bound: ExpressionLike, name: str | None = None) IterableSpace[Quantifier]

A range of values

Parameters:
  • lower_bound – The range’s inclusive lower bound

  • upper_bound – The range’s inclusive upper bound

  • name – An optional name to create an alias for this interval

opvious.modeling.literal(val: float | int) Expression

Wraps a literal value into an expression

Arg:

val: float or integer

In general you will not need to use this method as expression operators automatically call this under the hood.

opvious.modeling.method_decorator(require_call=False)

Transforms a decorator into a method-friendly equivalent

opvious.modeling.relabel(fragment: ModelFragment, **kwargs: str) ModelFragment

Updates a fragment’s definitions’ labels

Parameters:
  • fragment – The fragment containing definitions to relabel

  • kwargs – Dictionary from old label to new label

opvious.modeling.size(quantifiable: Quantifiable) Expression

Returns the cardinality of the quantifiable as an expression

opvious.modeling.switch(*cases: tuple[Predicate, ExpressionLike] | ExpressionLike) Expression

Returns an expression allowing branching between different values

Parameters:

cases – Tuples of expressions and optionally, predicate. The expression for the first predicate that matches will be used. A missing predicate always matches.

opvious.modeling.total(body: Quantified[Expression]) Expression

Returns an expression representing the sum of the quantified input

Parameters:

body – A quantified expression to be summed over

opvious.modeling.fragments

Built-in model fragments

This module exports ModelFragment instances for common use-cases.

class opvious.modeling.fragments.ActivationVariable(tensor: TensorLike, *quantifiables: Quantifiable, upper_bound: ExpressionLike | TensorLike | bool = True, lower_bound: ExpressionLike | TensorLike | bool = False, name: str | None = None, negate=False, projection: Projection = -1)

Bases: ModelFragment

Indicator variable activation fragment

This variable tracks an underlying tensor or tensor-like expression. Assuming both of its bounds are defined (see below) it will be equal to 1 iff the underlying tensor is positive and 0 otherwise.

Parameters:
  • tensor – Tensor-like

  • quantifiables – Tensor quantifiables, can be omitted if the tensor is a variable or parameter

  • upper_bound – Value of the upper bound used in the activation constraint. If True the variable’s image’s upper bound will be used, if False no activation constraint will be added.

  • lower_bound – Value of the lower bound used in the deactivation constraint. If True the variable’s image’s lower bound will be used, if False no deactivation constraint will be added.

  • name – Name of the generated activation variable

  • negate – Negate the returned indicator variable.

  • projection – Mask used to project the variable’s quantification. When this is set, the indicator variable will be set to 1 iff at least one of the projected tensor values is positive.

property activates: Constraint | None

Constraint ensuring that the activation variable activates

This constraint enforces that the activation variable is set to 1 when at least one the underlying tensor’s value is positive.

property deactivates: Constraint | None

Constraint ensuring that the activation variable deactivates

This constraint enforces that the activation variable is set to 0 when none of the underlying tensor’s values are non-zero. It requires the fragment to have a non-zero lower bound.

property value: Variable

Activation variable value

As a convenience calling the fragment directly also returns expressions from this variable.

class opvious.modeling.fragments.DerivedVariable(body: Callable[[...], Any], *quantifiables: Quantifiable, name: str | None = None, image: Image = Image(lower_bound=-inf, upper_bound=inf, is_integral=False))

Bases: ModelFragment

Variable equal to a given equation

Parameters:
  • body – The equation defining the variable’s value

  • quantifiables – Variable quantification

  • name – Name of the generated variable

  • image – Generated variable Image

class opvious.modeling.fragments.MagnitudeVariable(tensor: TensorLike, *quantifiables: Quantifiable, name: str | None = None, image: Image | None = None, projection: Projection = -1, lower_bound=True, upper_bound=True)

Bases: ModelFragment

Absolute value variable fragment

Parameters:
  • tensor – Non-negative tensor-like

  • quantifiables – Tensor quantifiables. Can be omitted if tensor is a variable or parameter.

  • image – Tensor image. Defaults to tensor’s if it is a variable or parameter, else non-negative reals.

  • name – Name of the generated magnitude variable

  • projection – Mask used to project the variable’s quantification

  • lower_bound – Disable the lower bound

  • upper_bound – Disable the upper bound

See also magnitude_variable() for a decorator equivalent.

property value: Variable

The magnitude variable

class opvious.modeling.fragments.MaskedSubspace(*quantifiables: Quantifiable, alias_name: str | None = None)

Bases: ModelFragment

Masked subspace fragment

Parameters:
  • quantifiables – Underlying quantifiable

  • alias_name – Optional name for the masked subset

property mask: Parameter

Parameter controlling the subset’s element

class opvious.modeling.fragments.PiecewiseLinear(tensor: TensorLike, *quantifiables: Quantifiable, assume_convex=False, pieces_name: str | None = None, piece_count_name: str | None = None, component_name: str | None = None, factor_name: str | None = None, width_name: str | None = None)

Bases: ModelFragment

Multiplication with a piecewise-linear factor

Parameters:
  • tensor – Tensor-like

  • quantifiables – Underlying quantifiable

  • assume_convex – Assume that the factors are increasing

  • component_name – Name of the generated variable used to represent the value in each segmented piece.

  • pieces_name – Name of the interval representing all piece indices.

  • piece_count_name – Name of the generated piece count parameter.

  • factor_name – Name of the generated factor parameter.

  • width_name – Name of the generated width parameter.

property component: Variable

The underlying segmented variable

property factor: Parameter

The factor to multiply each component with

property piece_count: Parameter

The total number of pieces

total()

Returns the fully quantified piecewise-linear sum

property width: Parameter

The width of each segment for the resulting variable

opvious.transformations

Built-in transformations

This module exports all available ProblemTransformation instances.

class opvious.transformations.ConstrainObjective(target: Target, min_value: float = -inf, max_value: float = inf)

Bases: ProblemTransformation

A transformation which bounds the value of a objective

This can be used for example to guarantee a minimum objective levels when multiple objectives are involved or to implement custom multi-objective strategies (see Weighted distance multi-objective optimization).

max_value: float = inf

The objective’s maximum allowed value

min_value: float = -inf

The objective’s minimum allowed value

target: Target

The label of the objective to constrain

class opvious.transformations.DensifyVariables(labels: list[str] = <factory>)

Bases: ProblemTransformation

A transformation which updates one or more variables to be continuous

labels: list[str]

The labels of the variables to densify

If empty, all integral variables will be densified.

class opvious.transformations.OmitConstraints(labels: list[str] = <factory>)

Bases: ProblemTransformation

A transformation which drops one or more constraints

Any parameters or variables which are not referenced in any remaining constraint or objective will automatically be dropped.

labels: list[str]

The labels of the constraints to drop

If empty, all constraints will be dropped.

class opvious.transformations.OmitObjectives(labels: list[str] = <factory>)

Bases: ProblemTransformation

A transformation which drops one or more objectives

Similar to OmitConstraints, any parameters or variables which are not referenced in any remaining constraint or objective will automatically be dropped.

labels: list[str]

The labels of the objectives to drop

If empty, all objectives will be dropped.

class opvious.transformations.PinVariables(labels: list[str] = <factory>)

Bases: ProblemTransformation

A transformation which pins one or more variables to input values

Each pinned variable will have an associated derived parameter labeled $label_pin with the same domain and image as the original variable. For example a non-negative production variable would have a non-negative parameter labeled production_pin.

A constraint (labeled $label_isPinned) is also automatically added which enforces equality between the variable and any inputs passed in to the parameter. Values for keys which do not have a pin remain free, so it is possible to do partial pinning. For example, assuming a monthly variable labeled production, the following code would only pin production in January (to 100):

transformations = [opvious.transformations.PinVariables("production")]
parameters = {
    "production_pin": {"january": 100},
    # ...
}

Finally, note that this constraint can also be transformed similar to any other constraint. For example it can sometimes be useful to relax it via RelaxConstraints.

labels: list[str]

The labels of the variables to pin

If empty, all variables will be pinned.

class opvious.transformations.ProblemTransformation

Bases: object

Base problem transformation class

You should not need to interact with this class directly, instead use one of the available transformation subclasses.

class opvious.transformations.RelaxConstraints(labels: list[str] = <factory>, penalty: ~typing.Literal['TOTAL_DEVIATION', 'MAX_DEVIATION', 'DEVIATION_CARDINALITY'] = 'TOTAL_DEVIATION', is_capped: bool = False)

Bases: ProblemTransformation

A transformation which relaxes one or more constraints

Each relaxed constraint will be omitted from the formulation and replaced by a slack variable and an objective minimizing this slack (two of each for equality constraints). The derived variables have the same domain as the relaxed constraint, are always non-negative, and are labeled as follows:

  • $label_deficit for deficit slack (applicable for greater than and equality constraints).

  • $label_surplus for surplus slack (applicable for less than and equality constraints).

For example an equality constraint labeled isBalanced would be transformed into two variables labeled isBalanced_deficit and isBalanced_surplus. A greater than constraint labeled demandIsMet would be relaxed into a single variable labeled demandIsMet_deficit.

Each deficit (resp. surplus) variable has a corresponding objective labeled $label_minimizeDeficit (resp. $label_minimizeSurplus). Refer to the penalty parameter for details on how slack is penalized.

Finally, since relaxed formulations will almost always have multiple objectives, you may also need to specific a opvious.SolveStrategy. A common pattern is to omit any existing objectives and minimize the aggregate slack violation (see Detecting infeasibilities).

is_capped: bool = False

Whether slack is capped

Setting this to true will create an additional parameter for each slack variable labeled $label_deficitCap (resp. $label_surplusCap) for deficit (resp. surplus). This parameter has the same domain as the variable and is non-negative.

This option is required when using the DEVIATION_CARDINALITY penalty.

labels: list[str]

The labels of the constraints to relax

If empty, all constraints will be relaxed.

penalty: Literal['TOTAL_DEVIATION', 'MAX_DEVIATION', 'DEVIATION_CARDINALITY'] = 'TOTAL_DEVIATION'

Slack penalization mode

  • TOTAL_DEVIATION: Cost proportional to the total sum of the (absolute value of) slack for the constraint.

  • MAX_DEVIATION: Cost proportional to the maximum deviation for the constraint.

  • DEVIATION_CARDINALITY: Cost proportional to the number of rows with non-zero deviation. This penalty requires the relaxation to be capped.

The default is TOTAL_DEVIATION.