Modeling
This page provides an overview of the SDK’s declarative modeling API. The
functionality covered here is exported by the opvious.modeling
module, which
we recommend importing qualified:
import opvious.modeling as om
Definitions
Each Model
can be roughly thought of as a container
for definitions. Its specification is generated by combining all its attributes
which contain Definition
instances.
Note
Attributes starting with _
are ignored when detecting a model’s definitions.
There are 6 categories of definitions, all described below.
Dimensions
- 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
Parameters and variables
- 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.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.
Constraints
- 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
- opvious.modeling.constraint(*args, **kwargs)
Objectives
- 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)
- opvious.modeling.objective(*args, **kwargs)
Aliases
- opvious.modeling.alias(*args, **kwargs)
Terms
Expressions
- class opvious.modeling.Expression
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.
Quantifiers
- class opvious.modeling.Quantifier(identifier: QuantifierIdentifier)
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.
Predicates
- class opvious.modeling.Predicate
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.
Fragments
- class opvious.modeling.ModelFragment
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.