API reference
opvious
- class opvious.AbortedOutcome
Bases:
objectThe solve was cancelled before a solution was found
- class opvious.Client(executor: Executor)
Bases:
objectOpvious 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_DEFINITIONcode).
- property authenticated: bool
Returns true if the client is using a non-empty API token
- async cancel_solve(uuid: Uuid) 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 available
Executor- 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
writer – Writable output
transformations – Transformations to apply to the specification
- async fetch_solve_inputs(uuid: Uuid) SolveInputs
Retrieves a queued solve’s inputs
- Parameters:
uuid – The target queued solve’s UUID
- async fetch_solve_outputs(uuid: Uuid) 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: bool = False) str
Returns the problem’s annotated representation in LP format
- Parameters:
problem –
Probleminstance 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_ENDPOINTenvironment variable is unset or empty and no default endpoint was set.
- async paginate_formulation_solves(name: str, annotations: list[Annotation] | 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[Annotation] | 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: Uuid)
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[Annotation] | None = None) Uuid
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 –
Probleminstance to solveannotations – Optional annotations to tag the solve with. These can be used later to filter solves
The returned
QueuedSolveinstance 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) Json
Returns a serialized representation of the problem
The returned JSON object is a valid
SolveCandidatevalue and can be used to call the REST API directly.- Parameters:
problem –
Probleminstance to serialize
- async solve(problem: Problem, assert_feasible: bool = False, prefer_streaming: bool = True) Solution
Solves an optimization problem remotely
Inputs will be validated before being sent to the API for solving.
- Parameters:
problem –
Probleminstance 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 –
Probleminstance to summarize
- async wait_for_solve_outcome(uuid: Uuid, assert_feasible: bool = 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:
objectConstraint 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)
Bases:
objectThe 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: Value | None, relative_gap: Value | None)
Bases:
objectA solution was found
- objective_value: Value | None
The solution’s objective value
- optimal: bool
Whether this solution was optimal (within gap thresholds)
- relative_gap: Value | None
The solution’s relative gap (0.1 is 10%)
- class opvious.FormulationSpecification(formulation_name: str, tag_name: str | None = None)
Bases:
objectA 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:
objectNo feasible solution exists
- class opvious.LocalSpecification(sources: Sequence[LocalSpecificationSource], description: str | None = None, annotation: LocalSpecificationAnnotation | None = None)
Bases:
objectA 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:
objectAn issue detected within a specification
- class opvious.LocalSpecificationSource(text: str, title: str = 'untitled')
Bases:
objectA 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.LocalSpecificationStyle(*values)
Bases:
EnumSpecification rendering style
- enable() None
Use this rendering style for all specifications
- static reset() None
Clear any rendering style override
- class opvious.Problem(specification: Specification, parameters: Mapping[Label, TypeAliasForwardRef('TensorArgument')] | None = None, dimensions: Mapping[Label, TypeAliasForwardRef('DimensionArgument')] | None = None, transformations: list[ProblemTransformation] | None = None, strategy: SolveStrategy | None = None, options: SolveOptions | None = None)
Bases:
objectAn optimization problem instance
- dimensions: Mapping[Label, 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[Label, 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[Label, ObjectiveOutline], dimensions: Mapping[Label, DimensionOutline], parameters: Mapping[Label, TensorOutline], variables: Mapping[Label, TensorOutline], constraints: Mapping[Label, ConstraintOutline])
Bases:
objectModel metadata
- constraints: Mapping[Label, ConstraintOutline]
Constraint metadata, keyed by constraint label
- dimensions: Mapping[Label, DimensionOutline]
Dimension metadata, keyed by dimension label
- objectives: Mapping[Label, ObjectiveOutline]
Objective metadata, if applicable
- parameters: Mapping[Label, TensorOutline]
Parameter metadata, keyed by parameter label
- variables: Mapping[Label, 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:
objectReified 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: Uuid, annotations: list[Annotation], outcome: TypeAliasForwardRef('SolveOutcome') | None, enqueued_at: datetime, dequeued_at: datetime | None, completed_at: datetime | None, problem_summary: ProblemSummary | None, options: Json, transformations: Json, strategy: Json)
Bases:
objectQueued optimization attempt
Solves are queued via
Client.queue_solve(), existing queued solves can be retrieved from their UUID viaClient.fetch_solve().- annotations: list[Annotation]
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: Uuid
The solve’s unique identifier
- class opvious.RemoteSpecification(url: str)
Bases:
objectA model specification from a remote URL
- url: str
The specification’s http(s) URL
- class opvious.Solution(status: SolveStatus, outcome: SolveOutcome, problem_summary: ProblemSummary, outputs: SolveOutputs | None = None)
Bases:
objectSolver 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: SolveStatus
Status string
- class opvious.SolveInputs(problem_outline: ProblemOutline, raw_parameters: list[Any], raw_dimensions: list[Any] | None)
Bases:
objectSolve input data
- dimension(label: Label) Index
Returns the dimension for a given label as a pandas Index
- parameter(label: Label) 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: Value | None, lp_iteration_count: int | None, cut_count: int | None)
Bases:
objectSolve 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: Value | 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:
objectSolving options
- absolute_gap_threshold: float | None = None
Absolute gap threshold below which a solution is considered optimal
See also
relative_gap_thresholdfor 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_thresholdfor 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:
objectSuccessful solve output data
- constraint(label: Label) DataFrame
Returns constraint results for a given label.
The returned dataframe always has a
slackcolumn with the constraint’s slack (0 values may be omitted). If applicable, it will also have adual_valuecolumn.
- 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: Label) DataFrame
Returns variable results for a given label
The returned dataframe always has a
valuecolumn with the variable’s values (0 values may be omitted). If applicable, it will also have adual_valuecolumn.
- class opvious.SolveStrategy(target: Target, sense: ObjectiveSense | None = None, epsilon_constraints: list[EpsilonConstraint] = <factory>)
Bases:
objectMulti-objective solving strategy
- epsilon_constraints: list[EpsilonConstraint]
All epsilon-constraints to apply
- classmethod equally_weighted_sum(sense: ObjectiveSense | None = None) Self
Returns a strategy optimizing the sum of all objectives
- sense: ObjectiveSense | None = None
Optimization sense
- target: Target
Target objective
- class opvious.Tensor(entries: list[Any], default_value: ExtendedFloat = 0)
Bases:
objectAn n-dimensional matrix
- default_value: ExtendedFloat = 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) Self
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
is_pin – Whether the tensor corresponds to a pinned variable’s parameter. This changes the default value from zero to minus infinity (unpinned).
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
pandasseries 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
pandasdataframe
Finally, all non-scalar tensor arguments can be wrapped into a tuple
(arg, default)to provide adefaultvalue to use when no matching key exists.
- class opvious.UnboundedOutcome
Bases:
objectNo bounded optimal solution exists
- exception opvious.UnexpectedSolveOutcomeError(outcome: SolveOutcome)
Bases:
ExceptionThe solve ended with an unexpected outcome
- opvious.load_notebook_models(path: str, root: str | None = None, allow_empty: bool = False, include_classes: bool = False, include_symbols: Sequence[str] = ()) SimpleNamespace
Loads all models from a notebook
- Parameters:
path – Path to the notebook, relative to
rootif 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
Modelsubclassesinclude_symbols – Also add values with these names
- opvious.solve_outcome_status(outcome: SolveOutcome) SolveStatus
Returns the status corresponding to a given outcome
opvious.executors
- class opvious.executors.BinaryExecutorResult(status: int, trace: str | None, reader: Any)
Bases:
ExecutorResultBinary execution result
- class opvious.executors.Executor(variant: str, endpoint: str, authorization: str | None = None, supports_streaming: bool = False)
Bases:
objectGeneric HTTP request executor
- property authenticated: bool
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: Headers | 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:
ExceptionLocal representation of an error during an executor’s request
- class opvious.executors.ExecutorResult(status: int, trace: str | None)
Bases:
objectRequest 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:
ExecutorResultUnary JSON execution result
- class opvious.executors.JsonSeqExecutorResult(status: int, trace: str | None, reader: Any)
Bases:
ExecutorResultStreaming JSON execution result
- class opvious.executors.PlainTextExecutorResult(status: int, trace: str | None, reader: Any)
Bases:
ExecutorResultPlain 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_csvdoes 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:
DefinitionOptimization constraint
Constraints are best created directly from
Modelmethods 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:
objectBase 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: Label | None = None, name: Name | None = None, is_numeric: bool = False)
Bases:
Definition,ScalarSpaceAn 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
Quantifiableand 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: Predicate | None = None)
Bases:
objectQuantification source
- class opvious.modeling.Expression
Bases:
objectBase expression
Expressions are typically created via
ParameterorVariableinstances. They can be combined using any of the following operations:Addition:
x + ySubstraction:
x - yMultiplication:
x * yModulo:
x % yInteger division:
x // yFloating division:
x / yPower:
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
Predicatefor the list of supported comparison operators.
- class opvious.modeling.Image(lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, is_integral: bool = False)
Bases:
objectA 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,GenericBase protocol for spaces which can also be directly iterated on
It is exposed mostly as a typing convenience for typing model fragments.
Spaceis 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:
objectAn 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
Clientmethods, for example to start a solve.- Parameters:
align – Embed generated statement definitions inside an
alignenvironment. 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:
objectReusable 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: Label | 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: ObjectiveSense, label: Label | None = None)
Bases:
DefinitionOptimization objective
Objectives are best created directly from
Modelmethods 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: Name | None = None, label: Label | None = None, qualifiers: Sequence[Label] | None = None)
Bases:
TensorAn optimization input parameter
- Parameters:
image – Target
Imagequantifiables – 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
Tensorconvenience 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:
objectA predicate on expressions
Instances of this class are generated by using comparison operators on
Expressioninstances. 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_itemsconstraint above uses a greater than predicate to filter the set of products where the constraint applies.
- class opvious.modeling.Quantification(_quantifiables: tuple[TypeAliasForwardRef('Quantifiable'), ...], _names: Mapping[int, Name], _projection: Projection, _lift: bool, __hash__: Any = None)
Bases:
SpaceCross-product quantification
- class opvious.modeling.Quantifier(identifier: QuantifierIdentifier)
Bases:
ExpressionAn 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:
objectBase 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: DefinitionCategory, label: Label, name: Name | None, text: str)
Bases:
objectA rendered definition
- category: DefinitionCategory
The type of definition this statement contains
- label: Label
The definition’s label
- name: Name | 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: Name | None = None, label: Label | None = None, qualifiers: Sequence[Label] | None = None)
Bases:
DefinitionBase tensor class
Calling a tensor returns an
Expressionwith 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: Quantifiable, lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, **kwargs) T
Returns a tensor with real image
- classmethod discrete(*quantifiables: Quantifiable, lower_bound: ExpressionLike = -inf, upper_bound: ExpressionLike = inf, **kwargs) T
Returns a tensor with integral image
- classmethod indicator(*quantifiables: Quantifiable, **kwargs: Any) T
Returns a tensor with
{0, 1}integral image
- classmethod natural(*quantifiables: Quantifiable, upper_bound: ExpressionLike = inf, **kwargs) T
Returns a tensor with non-negative integral image
- classmethod non_negative(*quantifiables: Quantifiable, upper_bound: ExpressionLike = inf, **kwargs) T
Returns a tensor with non-negative real image
- classmethod non_positive(*quantifiables: Quantifiable, lower_bound: ExpressionLike = -inf, **kwargs) T
Returns a tensor with non-positive real image
- quantifiables() tuple[TypeAliasForwardRef('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: bool = 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: Quantifiable, **kwargs: Any) T
Returns a tensor with
[0, 1]real image
- class opvious.modeling.Variable(image: Image, *quantifiables: Quantifiable, name: Name | None = None, label: Label | None = None, qualifiers: Sequence[Label] | None = None)
Bases:
TensorAn optimization output variable
- Parameters:
image – Target
Imagequantifiables – 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
Tensorconvenience 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[Name] | None = None, projection: Projection = -1, lift: bool = 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
Crossinstances. 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[Name] | None = None) Domain
Creates a domain from a quantifiable
- opvious.modeling.interval(lower_bound: ExpressionLike, upper_bound: ExpressionLike, name: Name | 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: bool = False) Any
Transforms a decorator into a method-friendly equivalent
- opvious.modeling.relabel(fragment: ModelFragment, **kwargs: Label) 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, TypeAliasForwardRef('ExpressionLike')] | TypeAliasForwardRef('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.ActivatedVariable(tensor: TensorLike, *quantifiables: Quantifiable, indicator: Tensor, indicator_projection: Projection = -1, upper_bound: TypeAliasForwardRef('ExpressionLike') | None = None, force_activation: bool = True, force_deactivation: bool = True, negate: bool = False, name: Name | None = None)
Bases:
ModelFragmentProduct of a tensor with an indicator variable
This derived variable is useful to linearize a product of two variables, one of them being an indicator, for use in a constraint. Be aware that this may make problems harder to solve.
- Parameters:
tensor – Non-negative tensor-like
quantifiables – Quantification
indicator – Indicator tensor
indicator_projection – Projection used to compute
indicator’s subscriptsupper_bound – Tensor upper bound, can be omitted if
tensoris aTensorinstancenegate – Negate the input indicator
force_activation – Add constraint to ensure that the derived variable is at least equal to
tensorwhenindicatoris non-zero. You may choose to omit this if the variable is already pushed up via other constraintsforce_deactivation – Add constraint to ensure that the derived variable is equal to 0 when the indicator is zero. You may choose to omit this if the variable is already pushed down via other constraints
- class opvious.modeling.fragments.ActivationVariable(tensor: TensorLike, *quantifiables: Quantifiable, upper_bound: TypeAliasForwardRef('ExpressionLike') | TypeAliasForwardRef('TensorLike') | bool = True, lower_bound: TypeAliasForwardRef('ExpressionLike') | TypeAliasForwardRef('TensorLike') | bool = False, name: Name | None = None, negate: bool = False, projection: Projection = -1)
Bases:
ModelFragmentIndicator 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
Truethe variable’s image’s upper bound will be used, ifFalseno activation constraint will be added.lower_bound – Value of the lower bound used in the deactivation constraint. If
Truethe variable’s image’s lower bound will be used, ifFalseno 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: TensorLike, *quantifiables: Quantifiable, name: Name | None = None, image: Image = Image(lower_bound=-inf, upper_bound=inf, is_integral=False))
Bases:
ModelFragmentVariable 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: Name | None = None, image: Image | None = None, projection: Projection = -1, lower_bound: bool = True, upper_bound: bool = True)
Bases:
ModelFragmentAbsolute value variable fragment
- Parameters:
tensor – Non-negative tensor-like
quantifiables – Tensor quantifiables. Can be omitted if
tensoris 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: Name | None = None)
Bases:
ModelFragmentMasked subspace fragment
- Parameters:
quantifiables – Underlying quantifiable
alias_name – Optional name for the masked subset
- property masked
Container for decorated instance attributes
- class opvious.modeling.fragments.PiecewiseLinear(tensor: TensorLike, *quantifiables: Quantifiable, assume_convex: bool = 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:
ModelFragmentMultiplication 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() Expression
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:
ProblemTransformationA 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[Label] = <factory>)
Bases:
ProblemTransformationA transformation which updates one or more variables to be continuous
- labels: list[Label]
The labels of the variables to densify
If empty, all integral variables will be densified.
- class opvious.transformations.OmitConstraints(labels: list[Label] = <factory>)
Bases:
ProblemTransformationA 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[Label]
The labels of the constraints to drop
If empty, all constraints will be dropped.
- class opvious.transformations.OmitObjectives(labels: list[Label] = <factory>)
Bases:
ProblemTransformationA 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[Label]
The labels of the objectives to drop
If empty, all objectives will be dropped.
- class opvious.transformations.PinVariables(labels: list[Label] = <factory>)
Bases:
ProblemTransformationA transformation which pins one or more variables to input values
Each pinned variable will have an associated derived parameter labeled
$label_pinwith the same domain and image as the original variable. For example a non-negativeproductionvariable 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[Label]
The labels of the variables to pin
If empty, all variables will be pinned.
- class opvious.transformations.ProblemTransformation
Bases:
objectBase problem transformation class
You should not need to interact with this class directly, instead use one of the available transformation subclasses.
- class opvious.transformations.ProblemTransformationContext
Bases:
objectContext available to
ProblemTransformationinstances
- class opvious.transformations.RelaxConstraints(labels: list[Label] = <factory>, penalty: Literal['TOTAL_DEVIATION', 'MAX_DEVIATION', 'DEVIATION_CARDINALITY']='TOTAL_DEVIATION', is_capped: bool = False)
Bases:
ProblemTransformationA 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_deficitfor deficit slack (applicable for greater than and equality constraints).$label_surplusfor surplus slack (applicable for less than and equality constraints).
For example an equality constraint labeled
isBalancedwould be transformed into two variables labeledisBalanced_deficitandisBalanced_surplus. A greater than constraint labeleddemandIsMetwould 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 thepenaltyparameter 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_CARDINALITYpenalty.
- labels: list[Label]
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.