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 using cross(). As a convenience, iterating on a dimension also yields a suitable quantifier. This allows creating simple constraints directly:

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

    @constraint
    def at_least_one_of_each(self):
        for p in self.products:  # Note the iteration here
            yield self.count(p) >= 1

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 the constraint() decorator.

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

    @constraint
    def at_least_one(self):
        yield total(self.count(p) for p in self.products) >= 1
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 the objective() decorator.

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

    @objective
    def minimize_total_count(self):
        return total(self.count(p) for p in self.products)
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 or Variable instances. They can be combined using any of the following operations:

  • Addition: x + y

  • Substraction: x - y

  • Multiplication: x * y

  • Modulo: x % y

  • Integer division: x // y

  • Floating division: x / y

  • Power: x ** y

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

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

See also Predicate for the list of supported comparison operators.

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 a Dimension). 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.