Optimization backend (corneto.backend)#
Optimization backends and the runtime-selected defaults.
- class corneto.backend.Backend(default_solver=None, sparse_class=<class 'scipy.sparse._csr.csr_array'>)#
Bases:
ABC- static get_symbols(expressions)#
- abstractmethod Constant(value, name=None)#
- abstractmethod Variable(name=None, shape=None, lb=None, ub=None, vartype=VarType.CONTINUOUS)#
Create a variable for optimization.
This method defines an optimization variable with optional bounds, type, and additional graph-related properties.
- Parameters:
name (Optional[str]) – The name of the variable. Defaults to None.
shape (Optional[Tuple[int, ...]]) – The shape of the variable as a tuple. Defaults to None.
lb (Optional[Union[float, np.ndarray]]) – The lower bound of the variable. Can be a scalar or an array. Defaults to None.
ub (Optional[Union[float, np.ndarray]]) – The upper bound of the variable. Can be a scalar or an array. Defaults to None.
vartype (VarType) – The type of the variable (e.g., continuous, integer). Defaults to VarType.CONTINUOUS.
- Returns:
The created variable symbol, to be used in further expressions or constraints.
- Return type:
CSymbol
- abstractmethod Parameter(name=None, shape=None, value=None)#
- TotalVariation(X, *, pairs, axis=-1, weights=None, name=None, exact=False)#
Build an entrywise graph total-variation expression.
Xmust be a nonempty, real affine vector or matrix expression created by this backend. Continuous, integer, and binary expressions are supported, whether or not their variables have bounds. The selected axis ofXis connected by the directedpairs. Each pair contributes the destination-minus-source signed difference, summed over the other axis; its magnitude contributes to variation. Reversing a pair reverses the signed difference but leaves its magnitude unchanged.The returned problem contains a continuous absolute-value bound and registered expressions for the signed differences, absolute bounds, per-pair variation, and weighted total variation. Add its total variation expression to an objective or constrain it explicitly; no objective is added automatically. The total scales linearly with the magnitude of
Xand the number of rows summed for each pair. Withexact=True, binary inputs use exact XOR magnitudes, including when maximizing variation or when a pair has zero weight.- Parameters:
X (CExpression) – A nonempty real affine vector or matrix expression from this backend.
pairs (Iterable[Tuple[int, int]]) – Nonempty iterable of
(source, destination)integer index pairs alongaxis. Self-pairs are rejected. Pair order and duplicates are preserved.axis (int) – Axis of
Xconnected bypairs; defaults to-1. Negative axes follow NumPy conventions.weights (Sequence[float] | ndarray | None) – Optional one-dimensional sequence or array with one finite, nonnegative real weight per pair. Defaults to unit weights. Scalars are rejected.
name (str | None) – Optional stable expression-name prefix. If omitted, a unique prefix is generated.
exact (bool) – If true, require binary inputs and enforce exact absolute differences independently of the objective. Binary variables are used directly; affine expressions are linked to auxiliary binary selectors, constraining every entry of X to zero or one. Nonbinary symbols are rejected. Defaults to false, which retains the continuous absolute-value bound formulation.
- Returns:
A problem with the linear absolute-value formulation and registered expressions named
<prefix>_differenceand<prefix>_abs_boundwith shape(R, E),<prefix>_variation_by_pairwith shape(E,), and scalar<prefix>_total_variation. HereEis the number of pairs andRis the size of the other axis (or one for a vector).- Return type:
ProblemDef
- Problem(constraints=None, objectives=None, expressions=None, weights=None, direction=Direction.MIN)#
- solve(p, solver=None, max_seconds=None, warm_start=False, verbosity=0, **options)#
- Flow(g, lb=0, ub=10, n_flows=1, values=False, shared_bounds=False, varname='_flow', create_nonzero_indicators=False, alias_flow='flow', alias_flow_ipos='positive_flow', alias_flow_ineg='negative_flow', alias_nonzero_flow='with_flow', indicator_tolerance=0.0001, force_matrix=False)#
- Acyclic0(g, P, indicator_positive_var_name=None, indicator_negative_var_name=None, acyclic_var_name='_dag_layer', max_parents=None, vertex_lb_dist=None, vertex_ub_dist=None)#
Create Acyclicity Constraint.
This function creates acyclicity constraints, ensuring that the selected edges form an acyclic graph, meaning there are no cycles on the given property. Acyclicity can be applied, for example, over flow constraints or signal properties.
- Parameters:
g (BaseGraph) – The graph that defines the problem.
P (ProblemDef) – The problem definition.
indicator_positive_var_name (str) – The name of the indicator variable, i.e., which edges are selected. Default is EXPR_NAME_FLOW_IPOS.
indicator_negative_var_name (str, optional) – The name of the indicator variable for negative flows. Default is None. If a negative flow appears, the source and target nodes of the edge are reversed. For example, A->B with positive flow implies order(B) > order(A), with negative flow it implies order(A) > order(B).
acyclic_var_name (str, optional) – The name of the acyclic variable. Default is VAR_DAG.
max_parents (Optional[Union[int, Dict[Any, int]]], optional) – The maximum number of parents per node. If an integer is provided, the maximum number of parents is the same for all nodes. If a dictionary is provided, the maximum number of parents can be different for each node. Default is None.
Returns
-------
ProblemDef – The problem definition with acyclic constraints.
Raises
------
NotImplementedError – If hyperedges are used.
- Return type:
ProblemDef
- Acyclic(g, P, indicator_positive_var_name=None, indicator_negative_var_name=None, acyclic_var_name='_dag_layer', max_parents=None, vertex_lb_dist=None, vertex_ub_dist=None)#
Create Acyclicity Constraint.
This function creates acyclicity constraints, ensuring that the selected edges form an acyclic graph, meaning there are no cycles on the given property. Acyclicity can be applied, for example, over flow constraints or signal properties.
- Parameters:
g (BaseGraph) – The graph that defines the problem.
P (ProblemDef) – The problem definition.
indicator_positive_var_name (str) – The name of the indicator variable, i.e., which edges are selected. Default is EXPR_NAME_FLOW_IPOS.
indicator_negative_var_name (str, optional) – The name of the indicator variable for negative flows. Default is None. If a negative flow appears, the source and target nodes of the edge are reversed. For example, A->B with positive flow implies order(B) > order(A), with negative flow it implies order(A) > order(B).
acyclic_var_name (str, optional) – The name of the acyclic variable. Default is VAR_DAG.
max_parents (Optional[Union[int, Dict[Any, int]]], optional) – The maximum number of parents per node. If an integer is provided, the maximum number of parents is the same for all nodes. If a dictionary is provided, the maximum number of parents can be different for each node. Default is None.
vertex_lb_dist (Optional[List[Dict[Any, int]]], optional) – A list (one entry per experiment) of dictionaries that assign a lower bound (minimum layer/distance) for each vertex.
vertex_ub_dist (Optional[List[Dict[Any, int]]], optional) – A list (one entry per experiment) of dictionaries that assign an upper bound (maximum layer/distance) for each vertex.
Returns
-------
ProblemDef – The problem definition with acyclic constraints.
Raises
------
NotImplementedError – If hyperedges are used.
- Return type:
ProblemDef
- AcyclicFlow(g, lb=0, ub=10, values=False, max_parents=None, vertex_lb_dist=None, varname='_flow', alias_flow='flow', alias_flow_ipos='positive_flow', alias_flow_ineg='negative_flow', alias_nonzero_flow='with_flow', indicator_tolerance=0.0001)#
- Parameters:
- Return type:
ProblemDef
- Indicator(V, indexes=None, suffix='_i', name=None)#
- ThresholdIndicator(V, *, indexes=None, epsilon=1.0, name=None, positive_name=None, negative_name=None)#
Create optional positive and negative threshold implications.
A positive binary equal to one requires the bounded value to be at least
epsilon. A negative binary equal to one requires it to be at most-epsilon. When both binaries are zero, the original bounds remain available, including values between the thresholds. Reaching a threshold does not require its binary to be one.epsilonmust be finite and positive, and may be an array broadcastable to the selected value shape. A vector of thresholds also accepts a backend’s single-column representation of a vector. The returned problem registers the sum of the mutually exclusive binaries undername(default<variable>_threshold), with directional symbols named<name>_positiveand<name>_negativeunless explicitly supplied. Implications use linear constraints derived from the finite bounds.
- ExactSupport(V, *, selected=None, indexes=None, epsilon=1.0, nonnegative=False, name=None, positive_name=None, negative_name=None)#
Link a bounded value to binary structural support with a gap.
selected == 0forces the value to zero.selected == 1forces its magnitude to be at leastepsilon. For nonnegative values this uses one binary per entry. Signed values use mutually exclusive positive and negative binaries and expose their sum asselected.epsilonmay be a scalar or an array explicitly broadcastable to the selected value shape. “Exact” refers to this mathematical minimum-magnitude gap; it is not a solver-tolerance guarantee, and values smaller than the requested gap may still be returned by a numerically inaccurate solve and should be checked by the caller.An existing binary selector symbol can be supplied to avoid introducing a redundant binary variable, which is useful when a method already has a shared structural edge-selection variable. An arbitrary selector expression is linked to an auxiliary binary selector, so fractional expression values make the model infeasible instead of relaxing the support constraints.
- SelectedFlow(g, *, lb=0, ub=10, n_flows=1, edge_indices=None, flow_blocks=None, selector_groups=None, epsilon=1.0, exact_support=True, selected=None, acyclic_graph=None, max_parents=None, flow_name='flow', selected_by_flow_name='selected_by_flow', selected_by_group_name=None, selected_any_name='selected_any', dag_name='_dag_layer')#
Create bounded flows with exact per-flow and shared support.
Flow conservation is imposed on
g. Exact support binaries are created only foredge_indicesso boundary edges do not consume unnecessary integer variables. Rows whose selected flow bounds are nonnegative use one binary per flow; rows that permit negative flow automatically use mutually exclusive positive and negative binaries. Setexact_support=Falseto use one bounded indicator per entry: nonzero flow still implies selection, but a selected entry may carry zero flow. This smaller formulation is suitable when a minimizing objective makes such false selections unattractive and flow direction is not otherwise required.flow_blockscan map multiple rectangular regions of the flow matrix to the same logical edge ordering.selector_groupscan then map block columns to shared selector columns. This supports layouts such as forward and reversed edge blocks without indicators on the unused off-diagonal blocks. Setselected_any_name=Nonewhen a union across selector columns is not needed.The union across flows can be linked to an existing shared selector. With signed acyclic flows, direction-specific unions are used so a negative flow orders the edge in reverse. No redundant structural-union binary is created in that case.
- Parameters:
g (BaseGraph)
n_flows (int)
flow_blocks (Iterable[Tuple[Iterable[int], slice | Iterable[int]]] | None)
epsilon (float)
exact_support (bool)
selected (CExpression | None)
acyclic_graph (BaseGraph | None)
flow_name (str)
selected_by_flow_name (str)
selected_by_group_name (str | None)
selected_any_name (str | None)
dag_name (str)
- Return type:
ProblemDef
- NonZeroIndicator(V, *args, indexes=None, suffix_pos='_ipos', suffix_neg='_ineg', tolerance=0.001)#
- linear_or(x, axis=None, varname='or', ignore_type=False)#
- Parameters:
x (CExpression)
axis (int | None)
- Return type:
ProblemDef
- linear_and(x, axis=None, varname='and')#
- Parameters:
x (CExpression)
axis (int | None)
- Return type:
ProblemDef
- linear_xor(x, axis=None, varname='xor', ignore_type=False)#
- Parameters:
x (CExpression)
axis (int | None)
- Return type:
ProblemDef
- zero_function()#
- Return type:
CExpression
- class corneto.backend.CvxpyBackend(default_solver=None, sparse_class=<class 'scipy.sparse._csr.csr_array'>)#
Bases:
Backend- Variable(name=None, shape=None, lb=None, ub=None, vartype=VarType.CONTINUOUS)#
Create a variable for optimization.
This method defines an optimization variable with optional bounds, type, and additional graph-related properties.
- Parameters:
name (Optional[str]) – The name of the variable. Defaults to None.
shape (Optional[Tuple[int, ...]]) – The shape of the variable as a tuple. Defaults to None.
lb (Optional[Union[float, np.ndarray]]) – The lower bound of the variable. Can be a scalar or an array. Defaults to None.
ub (Optional[Union[float, np.ndarray]]) – The upper bound of the variable. Can be a scalar or an array. Defaults to None.
vartype (VarType) – The type of the variable (e.g., continuous, integer). Defaults to VarType.CONTINUOUS.
- Returns:
The created variable symbol, to be used in further expressions or constraints.
- Return type:
CSymbol
- Parameter(name=None, shape=None, value=None)#
- class corneto.backend.PicosBackend(default_solver=None)#
Bases:
Backend- Parameters:
default_solver (str | None)
- Variable(name=None, shape=None, lb=None, ub=None, vartype=VarType.CONTINUOUS, variable=True)#
Create a variable for optimization.
This method defines an optimization variable with optional bounds, type, and additional graph-related properties.
- Parameters:
name (Optional[str]) – The name of the variable. Defaults to None.
shape (Optional[Tuple[int, ...]]) – The shape of the variable as a tuple. Defaults to None.
lb (Optional[Union[float, np.ndarray]]) – The lower bound of the variable. Can be a scalar or an array. Defaults to None.
ub (Optional[Union[float, np.ndarray]]) – The upper bound of the variable. Can be a scalar or an array. Defaults to None.
vartype (VarType) – The type of the variable (e.g., continuous, integer). Defaults to VarType.CONTINUOUS.
variable (bool)
- Returns:
The created variable symbol, to be used in further expressions or constraints.
- Return type:
CSymbol
- class corneto.backend.VarType(value)#
-
- INTEGER = 'integer'#
- CONTINUOUS = 'continuous'#
- BINARY = 'binary'#
- corneto.backend.available_backends()#
Return the installed and usable optimization backends.
Total variation#
Backend.TotalVariation builds entrywise L1 graph variation for a nonempty,
real affine CORNETO vector or matrix expression created by the same backend.
Continuous, integer, and binary expressions are supported, with or without
variable bounds. For a vector, the connected axis is its only axis. For a
matrix, axis selects the connected dimension and defaults to -1;
negative axes follow NumPy conventions. The remaining dimension is summed.
pairs must contain at least one directed (source, destination) pair
with integer indices in range. Empty input and self-pairs are rejected.
Duplicate and reversed pairs each contribute independently. The registered
signed difference is destination minus source; reversing a pair reverses that
signed value while leaving its magnitude unchanged. Each pair’s variation is
the sum of these magnitudes across the other axis. The total variation applies
the pair weights to those sums. The optional one-dimensional weights
sequence or array must contain one finite, nonnegative real value per pair;
scalars are rejected and omitted weights are all one.
Every result registers <name>_difference and <name>_abs_bound with
shape (R, E), <name>_variation_by_pair with shape (E,), and scalar
<name>_total_variation. Here E is the number of pairs and R is the
size of the other axis, or one for a vector. When name is omitted, a
unique prefix is generated. Explicit prefixes should be distinct in a
composed problem. No objective is added automatically.
By default (exact=False), the primitive adds continuous variables D and constraints
D >= difference and D >= -difference with D >= 0. Thus the
registered total variation is an upper bound until an objective or another
constraint tightens D; X does not need variable bounds. A positive
coefficient in a minimization objective
makes positively weighted entries tight at an optimum when other uses permit
it; zero weights need not be tight. A budget constraint total_variation <=
budget still gives the exact projected upper bound on actual variation.
Maximizing total variation or lower-bounding it does not force D to equal
the absolute difference, so diagnostics should be recomputed from the solved
difference expression. In a maximization objective, subtract a positive
variation penalty. Entrywise L1 graph variation encourages connected entries
to be piecewise constant. Its scale depends on the magnitude and units of
X, the number of entries summed across the other axis, and the chosen
weights; multiplying X by a scalar multiplies the variation by its
absolute value.
For binary selections, exact=True makes every absolute difference equal
to the pairwise XOR, independently of the objective and including zero-weight
pairs. Binary variables are accepted directly. Affine expressions (including
slices and sums of selection indicators) are linked to auxiliary binary
selectors, explicitly restricting every input entry to zero or one. Nonbinary
symbols are rejected; this mode does not compute exact absolute values for
general continuous or integer inputs.
For binary endpoints a and b, exact mode adds D <= a + b and
D <= 2 - a - b to the usual lower bounds. These force
D = abs(a - b) while keeping D continuous. The registered names and
shapes are unchanged. This permits maximizing switch counts or imposing a
lower bound on switching. For example, after constraining the union size to
its previously obtained optimum, use the following as the diagnostic’s sole
objective (CORNETO minimizes by default):
tv = backend.TotalVariation(
selected,
pairs=[(0, 1), (1, 2)],
axis=1,
exact=True,
name="switches",
)
problem += tv
problem.add_objective(-tv.expr.switches_total_variation)
For a continuous expression whose rows represent signals over three ordered conditions, connect adjacent columns and add the variation penalty to any existing model objective:
tv = backend.TotalVariation(
X,
pairs=[(0, 1), (1, 2)],
axis=1,
weights=[1.0, 2.0],
name="conditions",
)
problem += tv
problem.add_objective(tv.expr.conditions_total_variation, weight=0.5)
The same formulation works for binary expressions over an arbitrary condition graph. To constrain the total amount of switching, merge the returned problem and add a budget instead of adding an objective:
tv = backend.TotalVariation(
selected,
pairs=[(0, 1), (0, 2), (2, 3)],
axis=0,
name="switches",
)
problem += tv
problem += tv.expr.switches_total_variation <= budget