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.
- 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:
problem –
Problem
instance to formatinclude_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), andOPVIOUS_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:
problem –
Problem
instance to solve
The returned
QueuedSolve
instance can be used to:track progress via
Client.poll_solve()
,retrieve inputs via
Client.fetch_solve()
,retrieve outputs via
Client.fetch_solve_outputs()
(after successful completion).
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:
problem –
Problem
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:
problem –
Problem
instance to solveassert_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:
problem –
Problem
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 viaClient.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
anddual_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
anddual_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 adefault
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 CWDroot – 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
subclassesinclude_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 supporthttps://
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.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 theconstraint()
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:
dimensions
, input key setsparameters
, input tensorsvariables
, output tensorsconstraints
, linear constraintsobjectives
, linear or quadratic targetsaliases
, convenience shorthands
- 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 usingcross()
. 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
orVariable
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:
total()
for summationscross()
for quantifierssize()
for cardinality expressionsswitch()
for branching logic
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 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).
- 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 theobjective()
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 aDimension
). 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 (seecross()
):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
- 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, ifFalse
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, ifFalse
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.
- 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.
- 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
- 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.
- total()
Returns the fully quantified piecewise-linear sum
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-negativeproduction
variable would have a non-negative parameter labeledproduction_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 labeledproduction
, 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 labeledisBalanced_deficit
andisBalanced_surplus
. A greater than constraint labeleddemandIsMet
would be relaxed into a single variable labeleddemandIsMet_deficit
.Each deficit (resp. surplus) variable has a corresponding objective labeled
$label_minimizeDeficit
(resp.$label_minimizeSurplus
). Refer to thepenalty
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
.