From f58c43e3c4294cfd85516a10329d53649fc62598 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Thu, 12 Dec 2024 19:16:49 +0000 Subject: [PATCH] build based on e4ce984 --- dev/.documenter-siteinfo.json | 2 +- dev/MathOptInterface.pdf | Bin 1343444 -> 1343540 bytes dev/background/duality/index.html | 2 +- .../infeasibility_certificates/index.html | 2 +- dev/background/motivation/index.html | 2 +- dev/background/naming_conventions/index.html | 2 +- dev/changelog/index.html | 2 +- dev/developer/checklists/index.html | 2 +- dev/index.html | 2 +- dev/manual/constraints/index.html | 2 +- dev/manual/models/index.html | 2 +- dev/manual/modification/index.html | 2 +- dev/manual/solutions/index.html | 2 +- dev/manual/standard_form/index.html | 2 +- dev/manual/variables/index.html | 2 +- dev/reference/callbacks/index.html | 12 +- dev/reference/constraints/index.html | 10 +- dev/reference/errors/index.html | 32 ++-- dev/reference/models/index.html | 28 ++-- dev/reference/modification/index.html | 12 +- dev/reference/nonlinear/index.html | 52 +++---- dev/reference/standard_form/index.html | 140 +++++++++--------- dev/reference/variables/index.html | 16 +- dev/release_notes/index.html | 2 +- dev/submodules/Benchmarks/overview/index.html | 2 +- .../Benchmarks/reference/index.html | 6 +- .../Bridges/implementation/index.html | 2 +- .../Bridges/list_of_bridges/index.html | 36 ++--- dev/submodules/Bridges/overview/index.html | 2 +- dev/submodules/Bridges/reference/index.html | 88 +++++------ .../FileFormats/overview/index.html | 2 +- .../FileFormats/reference/index.html | 8 +- dev/submodules/Nonlinear/overview/index.html | 2 +- dev/submodules/Nonlinear/reference/index.html | 50 +++---- dev/submodules/Test/overview/index.html | 2 +- dev/submodules/Test/reference/index.html | 10 +- dev/submodules/Utilities/overview/index.html | 2 +- dev/submodules/Utilities/reference/index.html | 72 ++++----- dev/tutorials/bridging_constraint/index.html | 2 +- dev/tutorials/example/index.html | 2 +- dev/tutorials/implementing/index.html | 2 +- dev/tutorials/latency/index.html | 2 +- .../manipulating_expressions/index.html | 2 +- dev/tutorials/mathprogbase/index.html | 2 +- 44 files changed, 314 insertions(+), 314 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 61e76516e7..5f8c4047a7 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.9.4","generation_timestamp":"2024-12-10T04:03:03","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.9.4","generation_timestamp":"2024-12-12T19:10:10","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/MathOptInterface.pdf b/dev/MathOptInterface.pdf index 8723385844782de44825ba05ee0568814fbd48dd..d38978f25496edf6825cd4921707e95185f67ade 100644 GIT binary patch delta 118322 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�t0&;7vAd3L=>%&~( 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 g|RCP9MIW{x*DfxOn@o)5W*&;`tkdQT*k2cy&B` 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 U50|Qc6d(#QI5`R>B}Gq03d14**#H0l delta 118273 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 diff --git a/dev/background/duality/index.html b/dev/background/duality/index.html index b25529d27c..0c74babbf7 100644 --- a/dev/background/duality/index.html +++ b/dev/background/duality/index.html @@ -77,4 +77,4 @@ \max & \sum b_k y_k \\ \text{s.t.} \;\; & C+C^\top - \sum (A_k+A_k^\top) y_k \in \mathcal{S}_+ \\ & C-C^\top - \sum(A_k-A_k^\top) y_k = 0 -\end{align}\]

and we recover $Z = X + X^\top$.

+\end{align}\]

and we recover $Z = X + X^\top$.

diff --git a/dev/background/infeasibility_certificates/index.html b/dev/background/infeasibility_certificates/index.html index 1bcad66edb..7f1caf23bb 100644 --- a/dev/background/infeasibility_certificates/index.html +++ b/dev/background/infeasibility_certificates/index.html @@ -25,4 +25,4 @@ \end{align}\]

and:

\[-\sum_{i=1}^m b_i^\top (y_i + \eta d_i) > -\sum_{i=1}^m b_i^\top y_i,\]

for any feasible dual solution $y$. The latter simplifies to $-\sum_{i=1}^m b_i^\top d_i > 0$. For a maximization problem, the inequality is $\sum_{i=1}^m b_i^\top d_i < 0$. (Note that these are the same inequality, modulo a - sign.)

If the solver has found a certificate of primal infeasibility:

Note

The choice of whether to scale the ray $d$ to have magnitude 1 is left to the solver.

Infeasibility certificates of variable bounds

Many linear solvers (for example, Gurobi) do not provide explicit access to the primal infeasibility certificate of a variable bound. However, given a set of linear constraints:

\[\begin{align} l_A \le A x \le u_A \\ l_x \le x \le u_x, -\end{align}\]

the primal certificate of the variable bounds can be computed using the primal certificate associated with the affine constraints, $d$. (Note that $d$ will have one element for each row of the $A$ matrix, and that some or all of the elements in the vectors $l_A$ and $u_A$ may be $\pm \infty$. If both $l_A$ and $u_A$ are finite for some row, the corresponding element in `d must be 0.)

Given $d$, compute $\bar{d} = d^\top A$. If the bound is finite, a certificate for the lower variable bound of $x_i$ is $\max\{\bar{d}_i, 0\}$, and a certificate for the upper variable bound is $\min\{\bar{d}_i, 0\}$.

+\end{align}\]

the primal certificate of the variable bounds can be computed using the primal certificate associated with the affine constraints, $d$. (Note that $d$ will have one element for each row of the $A$ matrix, and that some or all of the elements in the vectors $l_A$ and $u_A$ may be $\pm \infty$. If both $l_A$ and $u_A$ are finite for some row, the corresponding element in `d must be 0.)

Given $d$, compute $\bar{d} = d^\top A$. If the bound is finite, a certificate for the lower variable bound of $x_i$ is $\max\{\bar{d}_i, 0\}$, and a certificate for the upper variable bound is $\min\{\bar{d}_i, 0\}$.

diff --git a/dev/background/motivation/index.html b/dev/background/motivation/index.html index 82b792a9aa..6801c16a14 100644 --- a/dev/background/motivation/index.html +++ b/dev/background/motivation/index.html @@ -1,2 +1,2 @@ -Motivation · MathOptInterface

Motivation

MathOptInterface (MOI) is a replacement for MathProgBase, the first-generation abstraction layer for mathematical optimization previously used by JuMP and Convex.jl.

To address a number of limitations of MathProgBase, MOI is designed to:

  • Be simple and extensible
    • unifying linear, quadratic, and conic optimization,
    • seamlessly facilitating extensions to essentially arbitrary constraints and functions (for example, indicator constraints, complementarity constraints, and piecewise-linear functions)
  • Be fast
    • by allowing access to a solver's in-memory representation of a problem without writing intermediate files (when possible)
    • by using multiple dispatch and avoiding requiring containers of non-concrete types
  • Allow a solver to return multiple results (for example, a pool of solutions)
  • Allow a solver to return extra arbitrary information via attributes (for example, variable- and constraint-wise membership in an irreducible inconsistent subset for infeasibility analysis)
  • Provide a greatly expanded set of status codes explaining what happened during the optimization procedure
  • Enable a solver to more precisely specify which problem classes it supports
  • Enable both primal and dual warm starts
  • Enable adding and removing both variables and constraints by indices that are not required to be consecutive
  • Enable any modification that the solver supports to an existing model
  • Avoid requiring the solver wrapper to store an additional copy of the problem data
+Motivation · MathOptInterface

Motivation

MathOptInterface (MOI) is a replacement for MathProgBase, the first-generation abstraction layer for mathematical optimization previously used by JuMP and Convex.jl.

To address a number of limitations of MathProgBase, MOI is designed to:

  • Be simple and extensible
    • unifying linear, quadratic, and conic optimization,
    • seamlessly facilitating extensions to essentially arbitrary constraints and functions (for example, indicator constraints, complementarity constraints, and piecewise-linear functions)
  • Be fast
    • by allowing access to a solver's in-memory representation of a problem without writing intermediate files (when possible)
    • by using multiple dispatch and avoiding requiring containers of non-concrete types
  • Allow a solver to return multiple results (for example, a pool of solutions)
  • Allow a solver to return extra arbitrary information via attributes (for example, variable- and constraint-wise membership in an irreducible inconsistent subset for infeasibility analysis)
  • Provide a greatly expanded set of status codes explaining what happened during the optimization procedure
  • Enable a solver to more precisely specify which problem classes it supports
  • Enable both primal and dual warm starts
  • Enable adding and removing both variables and constraints by indices that are not required to be consecutive
  • Enable any modification that the solver supports to an existing model
  • Avoid requiring the solver wrapper to store an additional copy of the problem data
diff --git a/dev/background/naming_conventions/index.html b/dev/background/naming_conventions/index.html index f704f85232..44c6c1b5ac 100644 --- a/dev/background/naming_conventions/index.html +++ b/dev/background/naming_conventions/index.html @@ -1,2 +1,2 @@ -Naming conventions · MathOptInterface

Naming conventions

MOI follows several conventions for naming functions and structures. These should also be followed by packages extending MOI.

Sets

Sets encode the structure of constraints. Their names should follow the following conventions:

  • Abstract types in the set hierarchy should begin with Abstract and end in Set, for example, AbstractScalarSet, AbstractVectorSet.
  • Vector-valued conic sets should end with Cone, for example, NormInfinityCone, SecondOrderCone.
  • Vector-valued Cartesian products should be plural and not end in Cone, for example, Nonnegatives, not NonnegativeCone.
  • Matrix-valued conic sets should provide two representations: ConeSquare and ConeTriangle, for example, RootDetConeTriangle and RootDetConeSquare. See Matrix cones for more details.
  • Scalar sets should be singular, not plural, for example, Integer, not Integers.
  • As much as possible, the names should follow established conventions in the domain where this set is used: for instance, convex sets should have names close to those of CVX, and constraint-programming sets should follow MiniZinc's constraints.
+Naming conventions · MathOptInterface

Naming conventions

MOI follows several conventions for naming functions and structures. These should also be followed by packages extending MOI.

Sets

Sets encode the structure of constraints. Their names should follow the following conventions:

  • Abstract types in the set hierarchy should begin with Abstract and end in Set, for example, AbstractScalarSet, AbstractVectorSet.
  • Vector-valued conic sets should end with Cone, for example, NormInfinityCone, SecondOrderCone.
  • Vector-valued Cartesian products should be plural and not end in Cone, for example, Nonnegatives, not NonnegativeCone.
  • Matrix-valued conic sets should provide two representations: ConeSquare and ConeTriangle, for example, RootDetConeTriangle and RootDetConeSquare. See Matrix cones for more details.
  • Scalar sets should be singular, not plural, for example, Integer, not Integers.
  • As much as possible, the names should follow established conventions in the domain where this set is used: for instance, convex sets should have names close to those of CVX, and constraint-programming sets should follow MiniZinc's constraints.
diff --git a/dev/changelog/index.html b/dev/changelog/index.html index 0464e7839e..03fbc631a5 100644 --- a/dev/changelog/index.html +++ b/dev/changelog/index.html @@ -27,4 +27,4 @@ end write(path, s) end -end

v0.9.22 (May 22, 2021)

This release contains backports from the ongoing development of the v0.10 release.

  • Improved type inference in Utilities, Bridges and FileFormats submodules to reduce latency.
  • Improved performance of Utilities.is_canonical.
  • Fixed Utilities.pass_nonvariable_constraints with bridged variables.
  • Fixed performance regression of Utilities.Model.
  • Fixed ordering of objective setting in parser.

v0.9.21 (April 23, 2021)

  • Added supports_shift_constant.
  • Improve performance of bridging quadratic constraints.
  • Add precompilation statements.
  • Large improvements to the documentation.
  • Fix a variety of inference issues, benefiting precompilation and reducing initial latency.
  • RawParameters are now ignored when resetting a CachingOptimizer. Previously, changing the underlying optimizer after RawParameters were set would throw an error.
  • Utilities.AbstractModel is being refactored. This may break users interacting with private fields of a model generated using @model.

v0.9.20 (February 20, 2021)

  • Improved performance of Utilities.ScalarFunctionIterator
  • Added support for compute_conflict to MOI layers
  • Added test with zero off-diagonal quadratic term in objective
  • Fixed double deletion of nested bridged SingleVariable/VectorOfVariables constraints
  • Fixed modification of un-set objective
  • Fixed function modification with duplicate terms
  • Made unit tests abort without failing if the problem class is not supported
  • Formatted code with JuliaFormatter
  • Clarified BasisStatusCode's docstring

v0.9.19 (December 1, 2020)

  • Added CallbackNodeStatus attribute
  • Added bridge from GreaterThan or LessThan to Interval
  • Added tests for infeasibility certificates and double optimize
  • Fixed support for Julia v1.6
  • Re-organized MOI docs and added documentation for adding a test

v0.9.18 (November 3, 2020)

  • Various improvements for working with complex numbers
  • Added GeoMeantoRelEntrBridge to bridge a GeometricMeanCone constraint to a relative entropy constraint

v0.9.17 (September 21, 2020)

  • Fixed CleverDict with variable of negative index value
  • Implement supports_add_constrained_variable for MockOptimizer

v0.9.16 (September 17, 2020)

  • Various fixes:
    • 32-bit support
    • CleverDict with abstract value type
    • Checks in test suite

v0.9.15 (September 14, 2020)

  • Bridges improvements:
    • (R)SOCtoNonConvexQuad bridge
    • ZeroOne bridge
    • Use supports_add_constrained_variable in LazyBridgeOptimizer
    • Exposed VariableBridgeCost and ConstraintBridgeCost attributes
    • Prioritize constraining variables on creation according to these costs
    • Refactor bridge debugging
  • Large performance improvements across all submodules
  • Lots of documentation improvements
  • FileFormats improvements:
    • Update MathOptFormat to v0.5
    • Fix supported objectives in FileFormats
  • Testing improvements:
    • Add name option for basic_constraint_test
  • Bug fixes and missing methods
    • Add length for iterators
    • Fix bug with duplicate terms
    • Fix order of LinearOfConstraintIndices

v0.9.14 (May 30, 2020)

  • Add a solver-independent interface for accessing the set of conflicting constraints an Irreducible Inconsistent Subsystem (#1056).
  • Bump JSONSchema dependency from v0.2 to v0.3 (#1090).
  • Documentation improvements:
    • Fix typos (#1054, #1060, #1061, #1064, #1069, #1070).
    • Remove the outdated recommendation for a package implementing MOI for a solver XXX to be called MathOptInterfaceXXX (#1087).
  • Utilities improvements:
    • Fix is_canonical for quadratic functions (#1081, #1089).
    • Implement add_constrained_variable[s] for CachingOptimizer so that it is added as constrained variables to the underlying optimizer (#1084).
    • Add support for custom objective functions for UniversalFallback (#1086).
    • Deterministic ordering of constraints in UniversalFallback (#1088).
  • Testing improvements:
    • Add NormOneCone/NormInfinityCone tests (#1045).
  • Bridges improvements:
    • Add bridges from Semiinteger and Semicontinuous (#1059).
    • Implement getting ConstraintSet for Variable.FlipSignBridge (#1066).
    • Fix setting ConstraintFunction for Constraint.ScalarizeBridge (#1093).
    • Fix NormOne/NormInf bridges with nonzero constants (#1045).
    • Fix StackOverflow in debug (#1063).
  • FileFormats improvements:
    • [SDPA] Implement the extension for integer variables (#1079).
    • [SDPA] Ignore comments after m and nblocks and detect dat-s extension (#1077).
    • [SDPA] No scaling of off-diagonal coefficient (#1076).
    • [SDPA] Add missing negation of constant (#1075).

v0.9.13 (March 24, 2020)

  • Added tests for Semicontinuous and Semiinteger variables (#1033).
  • Added tests for using ExprGraphs from NLP evaluators (#1043).
  • Update version compatibilities of dependencies (#1034, #1051, #1052).
  • Fixed typos in documentation (#1044).

v0.9.12 (February 28, 2020)

  • Fixed writing NLPBlock in MathOptFormat (#1037).
  • Fixed MockOptimizer for result attributes with non-one result index (#1039).
  • Updated test template with instantiate (#1032).

v0.9.11 (February 21, 2020)

  • Add an option for the model created by Utilities.@model to be a subtype of AbstractOptimizer (#1031).
  • Described dual cone in docstrings of GeoMeanCone and RelativeEntropyCone (#1018, #1028).
  • Fixed typos in documentation (#1022, #1024).
  • Fixed warning of unsupported attribute (#1027).
  • Added more rootdet/logdet conic tests (#1026).
  • Implemented ConstraintDual for Constraint.GeoMeanBridge, Constraint.RootDetBridge and Constraint.LogDetBridge and test duals in tests with GeoMeanCone and RootDetConeTriangle and LogDetConeTriangle cones (#1025, #1026).

v0.9.10 (January 31, 2020)

  • Added OptimizerWithAttributes grouping an optimizer constructor and a list of optimizer attributes (#1008).
  • Added RelativeEntropyCone with corresponding bridge into exponential cone constraints (#993).
  • Added NormSpectralCone and NormNuclearCone with corresponding bridges into positive semidefinite constraints (#976).
  • Added supports_constrained_variable(s) (#1004).
  • Added dual_set_type (#1002).
  • Added tests for vector specialized version of delete (#989, #1011).
  • Added PSD3 test (#1007).
  • Clarified dual solution of Tests.pow1v and Tests.pow1f (#1013).
  • Added support for EqualTo and Zero in Bridges.Constraint.SplitIntervalBridge (#1005).
  • Fixed Utilities.vectorize for empty vector (#1003).
  • Fixed free variables in LP writer (#1006).

v0.9.9 (December 29, 2019)

  • Incorporated MathOptFormat.jl as the FileFormats submodule. FileFormats provides readers and writers for a number of standard file formats and MOF, a file format specialized for MOI (#969).
  • Improved performance of deletion of vector of variables in MOI.Utilities.Model (#983).
  • Updated to MutableArithmetics v0.2 (#981).
  • Added MutableArithmetics.promote_operation allocation tests (#975).
  • Fixed inference issue on Julia v1.1 (#982).

v0.9.8 (December 19, 2019)

  • Implemented MutableArithmetics API (#924).
  • Fixed callbacks with CachingOptimizer (#959).
  • Fixed MOI.dimension for MOI.Complements (#948).
  • Added fallback for add_variables (#972).
  • Added is_diagonal_vectorized_index utility (#965).
  • Improved linear constraints display in manual (#963, #964).
  • Bridges improvements:
    • Added IndicatorSet to SOS1 bridge (#877).
    • Added support for starting values for Variable.VectorizeBridge (#944).
    • Fixed MOI.add_constraints with non-bridged variable constraint on bridged variable (#951).
    • Fixed corner cases and docstring of GeoMeanBridge (#961, #962, #966).
    • Fixed choice between variable or constraint bridges for constrained variables (#973).
    • Improve performance of bridge shortest path (#945, #946, #956).
    • Added docstring for test_delete_bridge (#954).
    • Added Variable bridge tests (#952).

v0.9.7 (October 30, 2019)

  • Implemented _result_index_field for NLPBlockDual (#934).
  • Fixed copy of model with starting values for vector constraints (#941).
  • Bridges improvements:
    • Improved performance of add_bridge and added has_bridge (#935).
    • Added AbstractSetMapBridge for bridges between sets S1, S2 such that there is a linear map A such that A*S1 = S2 (#933).
    • Added support for starting values for FlipSignBridge, VectorizeBridge, ScalarizeBridge, SlackBridge, SplitIntervalBridge, RSOCBridge, SOCRBridge NormInfinityBridge, SOCtoPSDBridge and RSOCtoPSDBridge (#933, #936, #937, #938, #939).

v0.9.6 (October 25, 2019)

  • Added complementarity constraints (#913).
  • Allowed ModelLike objects as value of attributes (#928).
  • Testing improvements:
    • Added dual_objective_value option to MOI.Test.TestConfig (#922).
    • Added InvalidIndex tests in basic_constraint_tests (#921).
    • Added tests for the constant term in indicator constraint (#929).
  • Bridges improvements:
    • Added support for starting values for Functionize bridges (#923).
    • Added variable indices context to variable bridges (#920).
    • Fixed a typo in printing o debug_supports (#927).

v0.9.5 (October 9, 2019)

  • Clarified PrimalStatus/DualStatus to be NO_SOLUTION if result_index is out of bounds (#912).
  • Added tolerance for checks and use ResultCount + 1 for the result_index in MOI.Test.solve_result_status (#910, #917).
  • Use 0.5 instead of 2.0 for power in PowerCone in basic_constraint_test (#916).
  • Bridges improvements:
    • Added debug utilities for unsupported variable/constraint/objective (#861).
    • Fixed deletion of variables in bridged VectorOfVariables constraints (#909).
    • Fixed result_index with objective bridges (#911).

v0.9.4 (October 2, 2019)

  • Added solver-independent MIP callbacks (#782).
  • Implements submit for Utilities.CachingOptimizer and Bridges.AbstractBridgeOptimizer (#906).
  • Added tests for result count of solution attributes (#901, #904).
  • Added NumberOfThreads attribute (#892).
  • Added Utilities.get_bounds to get the bounds on a variable (#890).
  • Added a note on duplicate coefficients in documentation (#581).
  • Added result index in ConstraintBasisStatus (#898).
  • Added extension dictionary to Utilities.Model (#884, #895).
  • Fixed deletion of constrained variables for CachingOptimizer (#905).
  • Implemented Utilities.shift_constraint for Test.UnknownScalarSet (#896).
  • Bridges improvements:
    • Added Variable.RSOCtoSOCBridge (#907).
    • Implemented MOI.get for ConstraintFunction/ConstraintSet for Bridges.Constraint.SquareBridge (#899).

v0.9.3 (September 20, 2019)

  • Fixed ambiguity detected in Julia v1.3 (#891, #893).
  • Fixed missing sets from ListOfSupportedConstraints (#880).
  • Fixed copy of VectorOfVariables constraints with duplicate indices (#886).
  • Added extension dictionary to MOIU.Model (#884).
  • Implemented MOI.get for function and set for GeoMeanBridge (#888).
  • Updated documentation for SingleVariable indices and bridges (#885).
  • Testing improvements:
    • Added more comprehensive tests for names (#882).
    • Added tests for SingleVariable duals (#883).
    • Added tests for DualExponentialCone and DualPowerCone (#873).
  • Improvements for arbitrary coefficient type:
    • Fixed == for sets with mutable fields (#887).
    • Removed some Float64 assumptions in bridges (#878).
    • Automatic selection of Constraint.[Scalar|Vector]FunctionizeBridge (#889).

v0.9.2 (September 5, 2019)

  • Implemented model printing for MOI.ModelLike and specialized it for models defined in MOI (864).
  • Generalized contlinear tests for arbitrary coefficient type (#855).
  • Fixed supports_constraint for Semiinteger and Semicontinuous and supports for ObjectiveFunction (#859).
  • Fixed Allocate-Load copy for single variable constraints (#856).
  • Bridges improvements:
    • Add objective bridges (#789).
    • Fixed Variable.RSOCtoPSDBridge for dimension 2 (#869).
    • Added Variable.SOCtoRSOCBridge (#865).
    • Added Constraint.SOCRBridge and disable MOI.Bridges.Constraint.SOCtoPSDBridge (#751).
    • Fixed added_constraint_types for Contraint.LogDetBridge and Constraint.RootDetBridge (#870).

v0.9.1 (August 22, 2019)

  • Fix support for Julia v1.2 (#834).
  • L1 and L∞ norm epigraph cones and corresponding bridges to LP were added (#818).
  • Added tests to MOI.Test.nametest (#833).
  • Fix MOI.Test.soc3test for solvers not supporting infeasibility certificates (#839).
  • Implements operate for operators * and / between vector function and constant (#837).
  • Implements show for MOI.Utilities.IndexMap (#847).
  • Fix corner cases for mapping of variables in MOI.Utilities.CachingOptimizer and substitution of variables in MOI.Bridges.AbstractBridgeOptimizer (#848).
  • Fix transformation of constant terms for MOI.Bridges.Constraint.SOCtoPSDBridge and MOI.Bridges.Constraint.RSOCtoPSDBridge (#840).

v0.9.0 (August 13, 2019)

  • Support for Julia v0.6 and v0.7 was dropped (#714, #717).
  • A MOI.Utilities.Model implementation of ModelLike, this should replace most use cases of MOI.Utilities.@model (#781).
  • add_constrained_variable and add_constrained_variables were added (#759).
  • Support for indicator constraints was added (#709, #712).
  • DualObjectiveValue attribute was added (#473).
  • RawParameter attribute was added (#733).
  • A dual_set function was added (#804).
  • A Benchmarks submodule was added to facilitate solver benchmarking (#769).
  • A submit function was added, this may for instance allow the user to submit solutions or cuts to the solver from a callback (#775).
  • The field of ObjectiveValue was renamed to result_index (#729).
  • The _constant and Utilities.getconstant function were renamed to constant
  • REDUCTION_CERTIFICATE result status was added (#734).
  • Abstract matrix sets were added (#731).
  • Testing improvements:
    • The testing guideline was updated (#728).
    • Quadratic tests were added (#697).
    • Unit tests for RawStatusString, SolveTime, Silent and SolverName were added (#726, #741).
    • A rotated second-order cone test was added (#759).
    • A power cone test was added (#768).
    • Tests for ZeroOne variables with variable bounds were added (#772).
    • An unbounded test was added (#773).
    • Existing tests had a few updates (#702, #703, #763).
  • Documentation improvements:
    • Added a section on CachingOptimizer (#777).
    • Added a section on UniversalFallback, Model and @model (#762).
    • Transition the knapsack example to a doctest with MockOptimizer (#786).
  • Utilities improvements:
    • A CleverDict utility was added for a vector that automatically transform into a dictionary once a first index is removed (#767).
    • The Utilities.constant function was renamed to Utilities.constant_vector (#740).
    • Implement optimizer attributes for CachingOptimizer (#745).
    • Rename Utilities.add_scalar_constraint to Utilities.normalize_and_add_constraint (#801).
    • operate with vcat, SingleVariable and VectorOfVariables now returns a VectorOfVariables (#616).
    • Fix a type piracy of operate (#784).
    • The load_constraint fallback signature was fixed (#760).
    • The set_dot function was extended to work with sparse arrays (#805).
  • Bridges improvements:
    • The bridges no longer store the constraint function and set before it is bridged, the bridges now have to implement ConstraintFunction and ConstraintSet if the user wants to recover them. As a consequence, the @bridge macro was removed (#722).
    • Bridge are now instantiated with a bridge_constraint function instead of using a constructor (#730).
    • Fix constraint attributes for bridges (#699).
    • Constraint bridges were moved to the Bridges/Constraint submodule so they should now inherit from MOI.Bridges.Constraint.Abstract and should implement MOI.Bridges.Constraint.concrete_bridge_type instead of MOI.Bridges.concrete_bridge_type (#756).
    • Variable bridges were added in (#759).
    • Various improvements (#746, #747).

v0.8.4 (March 13, 2019)

  • Performance improvement in default_copy_to and bridge optimizer (#696).
  • Add Silent and implement setting optimizer attributes in caching and mock optimizers (#695).
  • Add Functionize bridges (SingleVariable and VectorOfVariables) (#659).
  • Minor typo fixes (#694).

v0.8.3 (March 6, 2019)

  • Use zero constant in scalar constraint function of MOI.Test.copytest (#691).
  • Fix variable deletion with SingleVariable objective function (#690).
  • Fix LazyBridgeOptimizer with bridges that add no constraints (#689).
  • Error message improvements (#673, #685, #686, #688).
  • Documentation improvements (#682, #683, #687).
  • Basis status:
    • Remove VariableBasisStatus (#679).
    • Test ConstraintBasisStatus and implement it in bridges (#678).
  • Fix inference of NumberOfVariables and NumberOfConstraints (#677).
  • Implement division between a quadratic function and a number (#675).

v0.8.2 (February 7, 2019)

  • Add RawStatusString attribute (#629).
  • Do not set names to the optimizer but only to the cache in CachingOptimizer (#638).
  • Make scalar MOI functions act as scalars in broadcast (#646).
  • Add function utilities:
    • Implement Base.zero (#634), Base.iszero (#643), add missing arithmetic operations (#644, #645) and fix division (#648).
    • Add a vectorize function that turns a vector of ScalarAffineFunction into a VectorAffineFunction (#642).
  • Improve support for starting values:
    • Show a warning in copy when starting values are not supported instead of throwing an error (#630).
    • Fix UniversalFallback for getting an variable or constraint attribute set to no indices (#623).
    • Add a test in contlineartest with partially set VariablePrimalStart.
  • Bridges improvements:
    • Fix StackOverFlow in LazyBridgeOptimizer when there is a cycle in the graph of bridges.
    • Add Slack bridges (#610, #650).
    • Add FlipSign bridges (#658).
  • Add tests with duplicate coefficients in ScalarAffineFunction and VectorAffineFunction (#639).
  • Use tolerance to compare VariablePrimal in rotatedsoc1 test (#632).
  • Use a zero constant in ScalarAffineFunction of constraints in psdt2 (#622).

v0.8.1 (January 7, 2019)

  • Adding an NLP objective now overrides any objective set using the ObjectiveFunction attribute (#619).
  • Rename fullbridgeoptimizer into full_bridge_optimizer (#621).
  • Allow custom constraint types with full_bridge_optimizer (#617).
  • Add Vectorize bridge which transforms scalar linear constraints into vector linear constraints (#615).

v0.8.0 (December 18, 2018)

  • Rename all enum values to follow the JuMP naming guidelines for constants, for example, Optimal becomes OPTIMAL, and DualInfeasible becomes DUAL_INFEASIBLE.
  • Rename CachingOptimizer methods for style compliance.
  • Add an MOI.TerminationStatusCode called ALMOST_DUAL_INFEASIBLE.

v0.7.0 (December 13, 2018)

  • Test that MOI.TerminationStatus is MOI.OptimizeNotCalled before MOI.optimize! is called.
  • Check supports_default_copy_to in tests (#594).
  • Key pieces of information like optimality, infeasibility, etc., are now reported through TerminationStatusCode. It is typically no longer necessary to check the result statuses in addition to the termination status.
  • Add perspective dimension to log-det cone (#593).

v0.6.4 (November 27, 2018)

  • Add OptimizeNotCalled termination status (#577) and improve documentation of other statuses (#575).
  • Add a solver naming guideline (#578).
  • Make FeasibilitySense the default ObjectiveSense (#579).
  • Fix Utilities.@model and Bridges.@bridge macros for functions and sets defined outside MOI (#582).
  • Document solver-specific attributes (#580) and implement them in Utilities.CachingOptimizer (#565).

v0.6.3 (November 16, 2018)

  • Variables and constraints are now allowed to have duplicate names. An error is thrown only on lookup. This change breaks some existing tests. (#549)
  • Attributes may now be partially set (some values could be nothing). (#563)
  • Performance improvements in Utilities.Model (#549, #567, #568)
  • Fix bug in QuadtoSOC (#558).
  • New supports_default_copy_to method that optimizers should implement to control caching behavior.
  • Documentation improvements.

v0.6.2 (October 26, 2018)

  • Improve hygiene of @model macro (#544).
  • Fix bug in copy tests (#543).
  • Fix bug in UniversalFallback attribute getter (#540).
  • Allow all correct solutions for solve_blank_obj unit test (#537).
  • Add errors for Allocate-Load and bad constraints (#534).
  • [performance] Add specialized implementation of hash for VariableIndex (#533).
  • [performance] Construct the name to object dictionaries lazily in model (#535).
  • Add the QuadtoSOC bridge which transforms ScalarQuadraticFunction constraints into RotatedSecondOrderCone (#483).

v0.6.1 (September 22, 2018)

  • Enable PositiveSemidefiniteConeSquare set and quadratic functions in MOIB.fullbridgeoptimizer (#524).
  • Add warning in the bridge between PositiveSemidefiniteConeSquare and PositiveSemidefiniteConeTriangle when the matrix is almost symmetric (#522).
  • Modify MOIT.copytest to not add multiples constraints on the same variable (#521).
  • Add missing keyword argument in one of MOIU.add_scalar_constraint methods (#520).

v0.6.0 (August 30, 2018)

  • The MOIU.@model and MOIB.@bridge macros now support functions and sets defined in external modules. As a consequence, function and set names in the macro arguments need to be prefixed by module name.
  • Rename functions according to the JuMP style guide:
    • copy! with keyword arguments copynames and warnattributes -> copy_to with keyword arguments copy_names and warn_attributes;
    • set! -> set;
    • addvariable[s]! -> add_variable[s];
    • supportsconstraint -> supports_constraint;
    • addconstraint[s]! -> add_constraint[s];
    • isvalid -> is_valid;
    • isempty -> is_empty;
    • Base.delete! -> delete;
    • modify! -> modify;
    • transform! -> transform;
    • initialize! -> initialize;
    • write -> write_to_file; and
    • read! -> read_from_file.
  • Remove free! (use Base.finalize instead).
  • Add the SquarePSD bridge which transforms PositiveSemidefiniteConeTriangle constraints into PositiveSemidefiniteConeTriangle.
  • Add result fallback for ConstraintDual of variable-wise constraint, ConstraintPrimal and ObjectiveValue.
  • Add tests for ObjectiveBound.
  • Add test for empty rows in vector linear constraint.
  • Rework errors: CannotError has been renamed NotAllowedError and the distinction between UnsupportedError and NotAllowedError is now about whether the element is not supported (for example, it cannot be copied a model containing this element) or the operation is not allowed (either because it is not implemented, because it cannot be performed in the current state of the model, or because it cannot be performed for a specific index)
  • canget is removed. NoSolution is added as a result status to indicate that the solver does not have either a primal or dual solution available (See #479).

v0.5.0 (August 5, 2018)

  • Fix names with CachingOptimizer.
  • Cleanup thanks to @mohamed82008.
  • Added a universal fallback for constraints.
  • Fast utilities for function canonicalization thanks to @rdeits.
  • Renamed dimension field to side_dimension in the context of matrix-like sets.
  • New and improved tests for cases like duplicate terms and ObjectiveBound.
  • Removed cantransform, canaddconstraint, canaddvariable, canset, canmodify, and candelete functions from the API. They are replaced by a new set of errors that are thrown: Subtypes of UnsupportedError indicate unsupported operations, while subtypes of CannotError indicate operations that cannot be performed in the current state.
  • The API for copy! is updated to remove the CopyResult type.
  • Updates for the new JuMP style guide.

v0.4.1 (June 28, 2018)

  • Fixes vector function modification on 32 bits.
  • Fixes Bellman-Ford algorithm for bridges.
  • Added an NLP test with FeasibilitySense.
  • Update modification documentation.

v0.4.0 (June 23, 2018)

  • Helper constructors for VectorAffineTerm and VectorQuadraticTerm.
  • Added modify_lhs to TestConfig.
  • Additional unit tests for optimizers.
  • Added a type parameter to CachingOptimizer for the optimizer field.
  • New API for problem modification (#388)
  • Tests pass without deprecation warnings on Julia 0.7.
  • Small fixes and documentation updates.

v0.3.0 (May 25, 2018)

  • Functions have been redefined to use arrays-of-structs instead of structs-of-arrays.
  • Improvements to MockOptimizer.
  • Significant changes to Bridges.
  • New and improved unit tests.
  • Fixes for Julia 0.7.

v0.2.0 (April 24, 2018)

  • Improvements to and better coverage of Tests.
  • Documentation fixes.
  • SolverName attribute.
  • Changes to the NLP interface (new definition of variable order and arrays of structs for bound pairs and sparsity patterns).
  • Addition of NLP tests.
  • Introduction of UniversalFallback.
  • copynames keyword argument to MOI.copy!.
  • Add Bridges submodule.

v0.1.0 (February 28, 2018)

  • Initial public release.
  • The framework for MOI was developed at the JuMP-dev workshop at MIT in June 2017 as a sorely needed replacement for MathProgBase.
+end

v0.9.22 (May 22, 2021)

This release contains backports from the ongoing development of the v0.10 release.

  • Improved type inference in Utilities, Bridges and FileFormats submodules to reduce latency.
  • Improved performance of Utilities.is_canonical.
  • Fixed Utilities.pass_nonvariable_constraints with bridged variables.
  • Fixed performance regression of Utilities.Model.
  • Fixed ordering of objective setting in parser.

v0.9.21 (April 23, 2021)

  • Added supports_shift_constant.
  • Improve performance of bridging quadratic constraints.
  • Add precompilation statements.
  • Large improvements to the documentation.
  • Fix a variety of inference issues, benefiting precompilation and reducing initial latency.
  • RawParameters are now ignored when resetting a CachingOptimizer. Previously, changing the underlying optimizer after RawParameters were set would throw an error.
  • Utilities.AbstractModel is being refactored. This may break users interacting with private fields of a model generated using @model.

v0.9.20 (February 20, 2021)

  • Improved performance of Utilities.ScalarFunctionIterator
  • Added support for compute_conflict to MOI layers
  • Added test with zero off-diagonal quadratic term in objective
  • Fixed double deletion of nested bridged SingleVariable/VectorOfVariables constraints
  • Fixed modification of un-set objective
  • Fixed function modification with duplicate terms
  • Made unit tests abort without failing if the problem class is not supported
  • Formatted code with JuliaFormatter
  • Clarified BasisStatusCode's docstring

v0.9.19 (December 1, 2020)

  • Added CallbackNodeStatus attribute
  • Added bridge from GreaterThan or LessThan to Interval
  • Added tests for infeasibility certificates and double optimize
  • Fixed support for Julia v1.6
  • Re-organized MOI docs and added documentation for adding a test

v0.9.18 (November 3, 2020)

  • Various improvements for working with complex numbers
  • Added GeoMeantoRelEntrBridge to bridge a GeometricMeanCone constraint to a relative entropy constraint

v0.9.17 (September 21, 2020)

  • Fixed CleverDict with variable of negative index value
  • Implement supports_add_constrained_variable for MockOptimizer

v0.9.16 (September 17, 2020)

  • Various fixes:
    • 32-bit support
    • CleverDict with abstract value type
    • Checks in test suite

v0.9.15 (September 14, 2020)

  • Bridges improvements:
    • (R)SOCtoNonConvexQuad bridge
    • ZeroOne bridge
    • Use supports_add_constrained_variable in LazyBridgeOptimizer
    • Exposed VariableBridgeCost and ConstraintBridgeCost attributes
    • Prioritize constraining variables on creation according to these costs
    • Refactor bridge debugging
  • Large performance improvements across all submodules
  • Lots of documentation improvements
  • FileFormats improvements:
    • Update MathOptFormat to v0.5
    • Fix supported objectives in FileFormats
  • Testing improvements:
    • Add name option for basic_constraint_test
  • Bug fixes and missing methods
    • Add length for iterators
    • Fix bug with duplicate terms
    • Fix order of LinearOfConstraintIndices

v0.9.14 (May 30, 2020)

  • Add a solver-independent interface for accessing the set of conflicting constraints an Irreducible Inconsistent Subsystem (#1056).
  • Bump JSONSchema dependency from v0.2 to v0.3 (#1090).
  • Documentation improvements:
    • Fix typos (#1054, #1060, #1061, #1064, #1069, #1070).
    • Remove the outdated recommendation for a package implementing MOI for a solver XXX to be called MathOptInterfaceXXX (#1087).
  • Utilities improvements:
    • Fix is_canonical for quadratic functions (#1081, #1089).
    • Implement add_constrained_variable[s] for CachingOptimizer so that it is added as constrained variables to the underlying optimizer (#1084).
    • Add support for custom objective functions for UniversalFallback (#1086).
    • Deterministic ordering of constraints in UniversalFallback (#1088).
  • Testing improvements:
    • Add NormOneCone/NormInfinityCone tests (#1045).
  • Bridges improvements:
    • Add bridges from Semiinteger and Semicontinuous (#1059).
    • Implement getting ConstraintSet for Variable.FlipSignBridge (#1066).
    • Fix setting ConstraintFunction for Constraint.ScalarizeBridge (#1093).
    • Fix NormOne/NormInf bridges with nonzero constants (#1045).
    • Fix StackOverflow in debug (#1063).
  • FileFormats improvements:
    • [SDPA] Implement the extension for integer variables (#1079).
    • [SDPA] Ignore comments after m and nblocks and detect dat-s extension (#1077).
    • [SDPA] No scaling of off-diagonal coefficient (#1076).
    • [SDPA] Add missing negation of constant (#1075).

v0.9.13 (March 24, 2020)

  • Added tests for Semicontinuous and Semiinteger variables (#1033).
  • Added tests for using ExprGraphs from NLP evaluators (#1043).
  • Update version compatibilities of dependencies (#1034, #1051, #1052).
  • Fixed typos in documentation (#1044).

v0.9.12 (February 28, 2020)

  • Fixed writing NLPBlock in MathOptFormat (#1037).
  • Fixed MockOptimizer for result attributes with non-one result index (#1039).
  • Updated test template with instantiate (#1032).

v0.9.11 (February 21, 2020)

  • Add an option for the model created by Utilities.@model to be a subtype of AbstractOptimizer (#1031).
  • Described dual cone in docstrings of GeoMeanCone and RelativeEntropyCone (#1018, #1028).
  • Fixed typos in documentation (#1022, #1024).
  • Fixed warning of unsupported attribute (#1027).
  • Added more rootdet/logdet conic tests (#1026).
  • Implemented ConstraintDual for Constraint.GeoMeanBridge, Constraint.RootDetBridge and Constraint.LogDetBridge and test duals in tests with GeoMeanCone and RootDetConeTriangle and LogDetConeTriangle cones (#1025, #1026).

v0.9.10 (January 31, 2020)

  • Added OptimizerWithAttributes grouping an optimizer constructor and a list of optimizer attributes (#1008).
  • Added RelativeEntropyCone with corresponding bridge into exponential cone constraints (#993).
  • Added NormSpectralCone and NormNuclearCone with corresponding bridges into positive semidefinite constraints (#976).
  • Added supports_constrained_variable(s) (#1004).
  • Added dual_set_type (#1002).
  • Added tests for vector specialized version of delete (#989, #1011).
  • Added PSD3 test (#1007).
  • Clarified dual solution of Tests.pow1v and Tests.pow1f (#1013).
  • Added support for EqualTo and Zero in Bridges.Constraint.SplitIntervalBridge (#1005).
  • Fixed Utilities.vectorize for empty vector (#1003).
  • Fixed free variables in LP writer (#1006).

v0.9.9 (December 29, 2019)

  • Incorporated MathOptFormat.jl as the FileFormats submodule. FileFormats provides readers and writers for a number of standard file formats and MOF, a file format specialized for MOI (#969).
  • Improved performance of deletion of vector of variables in MOI.Utilities.Model (#983).
  • Updated to MutableArithmetics v0.2 (#981).
  • Added MutableArithmetics.promote_operation allocation tests (#975).
  • Fixed inference issue on Julia v1.1 (#982).

v0.9.8 (December 19, 2019)

  • Implemented MutableArithmetics API (#924).
  • Fixed callbacks with CachingOptimizer (#959).
  • Fixed MOI.dimension for MOI.Complements (#948).
  • Added fallback for add_variables (#972).
  • Added is_diagonal_vectorized_index utility (#965).
  • Improved linear constraints display in manual (#963, #964).
  • Bridges improvements:
    • Added IndicatorSet to SOS1 bridge (#877).
    • Added support for starting values for Variable.VectorizeBridge (#944).
    • Fixed MOI.add_constraints with non-bridged variable constraint on bridged variable (#951).
    • Fixed corner cases and docstring of GeoMeanBridge (#961, #962, #966).
    • Fixed choice between variable or constraint bridges for constrained variables (#973).
    • Improve performance of bridge shortest path (#945, #946, #956).
    • Added docstring for test_delete_bridge (#954).
    • Added Variable bridge tests (#952).

v0.9.7 (October 30, 2019)

  • Implemented _result_index_field for NLPBlockDual (#934).
  • Fixed copy of model with starting values for vector constraints (#941).
  • Bridges improvements:
    • Improved performance of add_bridge and added has_bridge (#935).
    • Added AbstractSetMapBridge for bridges between sets S1, S2 such that there is a linear map A such that A*S1 = S2 (#933).
    • Added support for starting values for FlipSignBridge, VectorizeBridge, ScalarizeBridge, SlackBridge, SplitIntervalBridge, RSOCBridge, SOCRBridge NormInfinityBridge, SOCtoPSDBridge and RSOCtoPSDBridge (#933, #936, #937, #938, #939).

v0.9.6 (October 25, 2019)

  • Added complementarity constraints (#913).
  • Allowed ModelLike objects as value of attributes (#928).
  • Testing improvements:
    • Added dual_objective_value option to MOI.Test.TestConfig (#922).
    • Added InvalidIndex tests in basic_constraint_tests (#921).
    • Added tests for the constant term in indicator constraint (#929).
  • Bridges improvements:
    • Added support for starting values for Functionize bridges (#923).
    • Added variable indices context to variable bridges (#920).
    • Fixed a typo in printing o debug_supports (#927).

v0.9.5 (October 9, 2019)

  • Clarified PrimalStatus/DualStatus to be NO_SOLUTION if result_index is out of bounds (#912).
  • Added tolerance for checks and use ResultCount + 1 for the result_index in MOI.Test.solve_result_status (#910, #917).
  • Use 0.5 instead of 2.0 for power in PowerCone in basic_constraint_test (#916).
  • Bridges improvements:
    • Added debug utilities for unsupported variable/constraint/objective (#861).
    • Fixed deletion of variables in bridged VectorOfVariables constraints (#909).
    • Fixed result_index with objective bridges (#911).

v0.9.4 (October 2, 2019)

  • Added solver-independent MIP callbacks (#782).
  • Implements submit for Utilities.CachingOptimizer and Bridges.AbstractBridgeOptimizer (#906).
  • Added tests for result count of solution attributes (#901, #904).
  • Added NumberOfThreads attribute (#892).
  • Added Utilities.get_bounds to get the bounds on a variable (#890).
  • Added a note on duplicate coefficients in documentation (#581).
  • Added result index in ConstraintBasisStatus (#898).
  • Added extension dictionary to Utilities.Model (#884, #895).
  • Fixed deletion of constrained variables for CachingOptimizer (#905).
  • Implemented Utilities.shift_constraint for Test.UnknownScalarSet (#896).
  • Bridges improvements:
    • Added Variable.RSOCtoSOCBridge (#907).
    • Implemented MOI.get for ConstraintFunction/ConstraintSet for Bridges.Constraint.SquareBridge (#899).

v0.9.3 (September 20, 2019)

  • Fixed ambiguity detected in Julia v1.3 (#891, #893).
  • Fixed missing sets from ListOfSupportedConstraints (#880).
  • Fixed copy of VectorOfVariables constraints with duplicate indices (#886).
  • Added extension dictionary to MOIU.Model (#884).
  • Implemented MOI.get for function and set for GeoMeanBridge (#888).
  • Updated documentation for SingleVariable indices and bridges (#885).
  • Testing improvements:
    • Added more comprehensive tests for names (#882).
    • Added tests for SingleVariable duals (#883).
    • Added tests for DualExponentialCone and DualPowerCone (#873).
  • Improvements for arbitrary coefficient type:
    • Fixed == for sets with mutable fields (#887).
    • Removed some Float64 assumptions in bridges (#878).
    • Automatic selection of Constraint.[Scalar|Vector]FunctionizeBridge (#889).

v0.9.2 (September 5, 2019)

  • Implemented model printing for MOI.ModelLike and specialized it for models defined in MOI (864).
  • Generalized contlinear tests for arbitrary coefficient type (#855).
  • Fixed supports_constraint for Semiinteger and Semicontinuous and supports for ObjectiveFunction (#859).
  • Fixed Allocate-Load copy for single variable constraints (#856).
  • Bridges improvements:
    • Add objective bridges (#789).
    • Fixed Variable.RSOCtoPSDBridge for dimension 2 (#869).
    • Added Variable.SOCtoRSOCBridge (#865).
    • Added Constraint.SOCRBridge and disable MOI.Bridges.Constraint.SOCtoPSDBridge (#751).
    • Fixed added_constraint_types for Contraint.LogDetBridge and Constraint.RootDetBridge (#870).

v0.9.1 (August 22, 2019)

  • Fix support for Julia v1.2 (#834).
  • L1 and L∞ norm epigraph cones and corresponding bridges to LP were added (#818).
  • Added tests to MOI.Test.nametest (#833).
  • Fix MOI.Test.soc3test for solvers not supporting infeasibility certificates (#839).
  • Implements operate for operators * and / between vector function and constant (#837).
  • Implements show for MOI.Utilities.IndexMap (#847).
  • Fix corner cases for mapping of variables in MOI.Utilities.CachingOptimizer and substitution of variables in MOI.Bridges.AbstractBridgeOptimizer (#848).
  • Fix transformation of constant terms for MOI.Bridges.Constraint.SOCtoPSDBridge and MOI.Bridges.Constraint.RSOCtoPSDBridge (#840).

v0.9.0 (August 13, 2019)

  • Support for Julia v0.6 and v0.7 was dropped (#714, #717).
  • A MOI.Utilities.Model implementation of ModelLike, this should replace most use cases of MOI.Utilities.@model (#781).
  • add_constrained_variable and add_constrained_variables were added (#759).
  • Support for indicator constraints was added (#709, #712).
  • DualObjectiveValue attribute was added (#473).
  • RawParameter attribute was added (#733).
  • A dual_set function was added (#804).
  • A Benchmarks submodule was added to facilitate solver benchmarking (#769).
  • A submit function was added, this may for instance allow the user to submit solutions or cuts to the solver from a callback (#775).
  • The field of ObjectiveValue was renamed to result_index (#729).
  • The _constant and Utilities.getconstant function were renamed to constant
  • REDUCTION_CERTIFICATE result status was added (#734).
  • Abstract matrix sets were added (#731).
  • Testing improvements:
    • The testing guideline was updated (#728).
    • Quadratic tests were added (#697).
    • Unit tests for RawStatusString, SolveTime, Silent and SolverName were added (#726, #741).
    • A rotated second-order cone test was added (#759).
    • A power cone test was added (#768).
    • Tests for ZeroOne variables with variable bounds were added (#772).
    • An unbounded test was added (#773).
    • Existing tests had a few updates (#702, #703, #763).
  • Documentation improvements:
    • Added a section on CachingOptimizer (#777).
    • Added a section on UniversalFallback, Model and @model (#762).
    • Transition the knapsack example to a doctest with MockOptimizer (#786).
  • Utilities improvements:
    • A CleverDict utility was added for a vector that automatically transform into a dictionary once a first index is removed (#767).
    • The Utilities.constant function was renamed to Utilities.constant_vector (#740).
    • Implement optimizer attributes for CachingOptimizer (#745).
    • Rename Utilities.add_scalar_constraint to Utilities.normalize_and_add_constraint (#801).
    • operate with vcat, SingleVariable and VectorOfVariables now returns a VectorOfVariables (#616).
    • Fix a type piracy of operate (#784).
    • The load_constraint fallback signature was fixed (#760).
    • The set_dot function was extended to work with sparse arrays (#805).
  • Bridges improvements:
    • The bridges no longer store the constraint function and set before it is bridged, the bridges now have to implement ConstraintFunction and ConstraintSet if the user wants to recover them. As a consequence, the @bridge macro was removed (#722).
    • Bridge are now instantiated with a bridge_constraint function instead of using a constructor (#730).
    • Fix constraint attributes for bridges (#699).
    • Constraint bridges were moved to the Bridges/Constraint submodule so they should now inherit from MOI.Bridges.Constraint.Abstract and should implement MOI.Bridges.Constraint.concrete_bridge_type instead of MOI.Bridges.concrete_bridge_type (#756).
    • Variable bridges were added in (#759).
    • Various improvements (#746, #747).

v0.8.4 (March 13, 2019)

  • Performance improvement in default_copy_to and bridge optimizer (#696).
  • Add Silent and implement setting optimizer attributes in caching and mock optimizers (#695).
  • Add Functionize bridges (SingleVariable and VectorOfVariables) (#659).
  • Minor typo fixes (#694).

v0.8.3 (March 6, 2019)

  • Use zero constant in scalar constraint function of MOI.Test.copytest (#691).
  • Fix variable deletion with SingleVariable objective function (#690).
  • Fix LazyBridgeOptimizer with bridges that add no constraints (#689).
  • Error message improvements (#673, #685, #686, #688).
  • Documentation improvements (#682, #683, #687).
  • Basis status:
    • Remove VariableBasisStatus (#679).
    • Test ConstraintBasisStatus and implement it in bridges (#678).
  • Fix inference of NumberOfVariables and NumberOfConstraints (#677).
  • Implement division between a quadratic function and a number (#675).

v0.8.2 (February 7, 2019)

  • Add RawStatusString attribute (#629).
  • Do not set names to the optimizer but only to the cache in CachingOptimizer (#638).
  • Make scalar MOI functions act as scalars in broadcast (#646).
  • Add function utilities:
    • Implement Base.zero (#634), Base.iszero (#643), add missing arithmetic operations (#644, #645) and fix division (#648).
    • Add a vectorize function that turns a vector of ScalarAffineFunction into a VectorAffineFunction (#642).
  • Improve support for starting values:
    • Show a warning in copy when starting values are not supported instead of throwing an error (#630).
    • Fix UniversalFallback for getting an variable or constraint attribute set to no indices (#623).
    • Add a test in contlineartest with partially set VariablePrimalStart.
  • Bridges improvements:
    • Fix StackOverFlow in LazyBridgeOptimizer when there is a cycle in the graph of bridges.
    • Add Slack bridges (#610, #650).
    • Add FlipSign bridges (#658).
  • Add tests with duplicate coefficients in ScalarAffineFunction and VectorAffineFunction (#639).
  • Use tolerance to compare VariablePrimal in rotatedsoc1 test (#632).
  • Use a zero constant in ScalarAffineFunction of constraints in psdt2 (#622).

v0.8.1 (January 7, 2019)

  • Adding an NLP objective now overrides any objective set using the ObjectiveFunction attribute (#619).
  • Rename fullbridgeoptimizer into full_bridge_optimizer (#621).
  • Allow custom constraint types with full_bridge_optimizer (#617).
  • Add Vectorize bridge which transforms scalar linear constraints into vector linear constraints (#615).

v0.8.0 (December 18, 2018)

  • Rename all enum values to follow the JuMP naming guidelines for constants, for example, Optimal becomes OPTIMAL, and DualInfeasible becomes DUAL_INFEASIBLE.
  • Rename CachingOptimizer methods for style compliance.
  • Add an MOI.TerminationStatusCode called ALMOST_DUAL_INFEASIBLE.

v0.7.0 (December 13, 2018)

  • Test that MOI.TerminationStatus is MOI.OptimizeNotCalled before MOI.optimize! is called.
  • Check supports_default_copy_to in tests (#594).
  • Key pieces of information like optimality, infeasibility, etc., are now reported through TerminationStatusCode. It is typically no longer necessary to check the result statuses in addition to the termination status.
  • Add perspective dimension to log-det cone (#593).

v0.6.4 (November 27, 2018)

  • Add OptimizeNotCalled termination status (#577) and improve documentation of other statuses (#575).
  • Add a solver naming guideline (#578).
  • Make FeasibilitySense the default ObjectiveSense (#579).
  • Fix Utilities.@model and Bridges.@bridge macros for functions and sets defined outside MOI (#582).
  • Document solver-specific attributes (#580) and implement them in Utilities.CachingOptimizer (#565).

v0.6.3 (November 16, 2018)

  • Variables and constraints are now allowed to have duplicate names. An error is thrown only on lookup. This change breaks some existing tests. (#549)
  • Attributes may now be partially set (some values could be nothing). (#563)
  • Performance improvements in Utilities.Model (#549, #567, #568)
  • Fix bug in QuadtoSOC (#558).
  • New supports_default_copy_to method that optimizers should implement to control caching behavior.
  • Documentation improvements.

v0.6.2 (October 26, 2018)

  • Improve hygiene of @model macro (#544).
  • Fix bug in copy tests (#543).
  • Fix bug in UniversalFallback attribute getter (#540).
  • Allow all correct solutions for solve_blank_obj unit test (#537).
  • Add errors for Allocate-Load and bad constraints (#534).
  • [performance] Add specialized implementation of hash for VariableIndex (#533).
  • [performance] Construct the name to object dictionaries lazily in model (#535).
  • Add the QuadtoSOC bridge which transforms ScalarQuadraticFunction constraints into RotatedSecondOrderCone (#483).

v0.6.1 (September 22, 2018)

  • Enable PositiveSemidefiniteConeSquare set and quadratic functions in MOIB.fullbridgeoptimizer (#524).
  • Add warning in the bridge between PositiveSemidefiniteConeSquare and PositiveSemidefiniteConeTriangle when the matrix is almost symmetric (#522).
  • Modify MOIT.copytest to not add multiples constraints on the same variable (#521).
  • Add missing keyword argument in one of MOIU.add_scalar_constraint methods (#520).

v0.6.0 (August 30, 2018)

  • The MOIU.@model and MOIB.@bridge macros now support functions and sets defined in external modules. As a consequence, function and set names in the macro arguments need to be prefixed by module name.
  • Rename functions according to the JuMP style guide:
    • copy! with keyword arguments copynames and warnattributes -> copy_to with keyword arguments copy_names and warn_attributes;
    • set! -> set;
    • addvariable[s]! -> add_variable[s];
    • supportsconstraint -> supports_constraint;
    • addconstraint[s]! -> add_constraint[s];
    • isvalid -> is_valid;
    • isempty -> is_empty;
    • Base.delete! -> delete;
    • modify! -> modify;
    • transform! -> transform;
    • initialize! -> initialize;
    • write -> write_to_file; and
    • read! -> read_from_file.
  • Remove free! (use Base.finalize instead).
  • Add the SquarePSD bridge which transforms PositiveSemidefiniteConeTriangle constraints into PositiveSemidefiniteConeTriangle.
  • Add result fallback for ConstraintDual of variable-wise constraint, ConstraintPrimal and ObjectiveValue.
  • Add tests for ObjectiveBound.
  • Add test for empty rows in vector linear constraint.
  • Rework errors: CannotError has been renamed NotAllowedError and the distinction between UnsupportedError and NotAllowedError is now about whether the element is not supported (for example, it cannot be copied a model containing this element) or the operation is not allowed (either because it is not implemented, because it cannot be performed in the current state of the model, or because it cannot be performed for a specific index)
  • canget is removed. NoSolution is added as a result status to indicate that the solver does not have either a primal or dual solution available (See #479).

v0.5.0 (August 5, 2018)

  • Fix names with CachingOptimizer.
  • Cleanup thanks to @mohamed82008.
  • Added a universal fallback for constraints.
  • Fast utilities for function canonicalization thanks to @rdeits.
  • Renamed dimension field to side_dimension in the context of matrix-like sets.
  • New and improved tests for cases like duplicate terms and ObjectiveBound.
  • Removed cantransform, canaddconstraint, canaddvariable, canset, canmodify, and candelete functions from the API. They are replaced by a new set of errors that are thrown: Subtypes of UnsupportedError indicate unsupported operations, while subtypes of CannotError indicate operations that cannot be performed in the current state.
  • The API for copy! is updated to remove the CopyResult type.
  • Updates for the new JuMP style guide.

v0.4.1 (June 28, 2018)

  • Fixes vector function modification on 32 bits.
  • Fixes Bellman-Ford algorithm for bridges.
  • Added an NLP test with FeasibilitySense.
  • Update modification documentation.

v0.4.0 (June 23, 2018)

  • Helper constructors for VectorAffineTerm and VectorQuadraticTerm.
  • Added modify_lhs to TestConfig.
  • Additional unit tests for optimizers.
  • Added a type parameter to CachingOptimizer for the optimizer field.
  • New API for problem modification (#388)
  • Tests pass without deprecation warnings on Julia 0.7.
  • Small fixes and documentation updates.

v0.3.0 (May 25, 2018)

  • Functions have been redefined to use arrays-of-structs instead of structs-of-arrays.
  • Improvements to MockOptimizer.
  • Significant changes to Bridges.
  • New and improved unit tests.
  • Fixes for Julia 0.7.

v0.2.0 (April 24, 2018)

  • Improvements to and better coverage of Tests.
  • Documentation fixes.
  • SolverName attribute.
  • Changes to the NLP interface (new definition of variable order and arrays of structs for bound pairs and sparsity patterns).
  • Addition of NLP tests.
  • Introduction of UniversalFallback.
  • copynames keyword argument to MOI.copy!.
  • Add Bridges submodule.

v0.1.0 (February 28, 2018)

  • Initial public release.
  • The framework for MOI was developed at the JuMP-dev workshop at MIT in June 2017 as a sorely needed replacement for MathProgBase.
diff --git a/dev/developer/checklists/index.html b/dev/developer/checklists/index.html index caafa2bf60..d69bb581c1 100644 --- a/dev/developer/checklists/index.html +++ b/dev/developer/checklists/index.html @@ -109,4 +109,4 @@ ## Documentation - - [ ] The version fields are updated in `docs/src/submodules/FileFormats/overview.md` + - [ ] The version fields are updated in `docs/src/submodules/FileFormats/overview.md` diff --git a/dev/index.html b/dev/index.html index a944ba5c26..5a4d888542 100644 --- a/dev/index.html +++ b/dev/index.html @@ -6,4 +6,4 @@ year={2021}, doi={10.1287/ijoc.2021.1067}, publisher={INFORMS} -}

A preprint of this paper is freely available.

+}

A preprint of this paper is freely available.

diff --git a/dev/manual/constraints/index.html b/dev/manual/constraints/index.html index 45b5c1a19f..19761397d5 100644 --- a/dev/manual/constraints/index.html +++ b/dev/manual/constraints/index.html @@ -19,4 +19,4 @@ false

Constraint attributes

The following attributes are available for constraints:

Get and set these attributes using get and set.

julia> MOI.set(model, MOI.ConstraintName(), c, "con_c")
 
 julia> MOI.get(model, MOI.ConstraintName(), c)
-"con_c"

Constraints by function-set pairs

Below is a list of common constraint types and how they are represented as function-set pairs in MOI. In the notation below, $x$ is a vector of decision variables, $x_i$ is a scalar decision variable, $\alpha, \beta$ are scalar constants, $a, b$ are constant vectors, A is a constant matrix and $\mathbb{R}_+$ (resp. $\mathbb{R}_-$) is the set of non-negative (resp. non-positive) real numbers.

Linear constraints

Mathematical ConstraintMOI FunctionMOI Set
$a^Tx \le \beta$ScalarAffineFunctionLessThan
$a^Tx \ge \alpha$ScalarAffineFunctionGreaterThan
$a^Tx = \beta$ScalarAffineFunctionEqualTo
$\alpha \le a^Tx \le \beta$ScalarAffineFunctionInterval
$x_i \le \beta$VariableIndexLessThan
$x_i \ge \alpha$VariableIndexGreaterThan
$x_i = \beta$VariableIndexEqualTo
$\alpha \le x_i \le \beta$VariableIndexInterval
$Ax + b \in \mathbb{R}_+^n$VectorAffineFunctionNonnegatives
$Ax + b \in \mathbb{R}_-^n$VectorAffineFunctionNonpositives
$Ax + b = 0$VectorAffineFunctionZeros

By convention, solvers are not expected to support nonzero constant terms in the ScalarAffineFunctions the first four rows of the preceding table because they are redundant with the parameters of the sets. For example, encode $2x + 1 \le 2$ as $2x \le 1$.

Constraints with VariableIndex in LessThan, GreaterThan, EqualTo, or Interval sets have a natural interpretation as variable bounds. As such, it is typically not natural to impose multiple lower- or upper-bounds on the same variable, and the solver interfaces will throw respectively LowerBoundAlreadySet or UpperBoundAlreadySet.

Moreover, adding two VariableIndex constraints on the same variable with the same set is impossible because they share the same index as it is the index of the variable, see ConstraintIndex.

It is natural, however, to impose upper- and lower-bounds separately as two different constraints on a single variable. The difference between imposing bounds by using a single Interval constraint and by using separate LessThan and GreaterThan constraints is that the latter will allow the solver to return separate dual multipliers for the two bounds, while the former will allow the solver to return only a single dual for the interval constraint.

Conic constraints

Mathematical ConstraintMOI FunctionMOI Set
$\lVert Ax + b\rVert_2 \le c^Tx + d$VectorAffineFunctionSecondOrderCone
$y \ge \lVert x \rVert_2$VectorOfVariablesSecondOrderCone
$2yz \ge \lVert x \rVert_2^2, y,z \ge 0$VectorOfVariablesRotatedSecondOrderCone
$(a_1^Tx + b_1,a_2^Tx + b_2,a_3^Tx + b_3) \in \mathcal{E}$VectorAffineFunctionExponentialCone
$A(x) \in \mathcal{S}_+$VectorAffineFunctionPositiveSemidefiniteConeTriangle
$B(x) \in \mathcal{S}_+$VectorAffineFunctionPositiveSemidefiniteConeSquare
$x \in \mathcal{S}_+$VectorOfVariablesPositiveSemidefiniteConeTriangle
$x \in \mathcal{S}_+$VectorOfVariablesPositiveSemidefiniteConeSquare

where $\mathcal{E}$ is the exponential cone (see ExponentialCone), $\mathcal{S}_+$ is the set of positive semidefinite symmetric matrices, $A$ is an affine map that outputs symmetric matrices and $B$ is an affine map that outputs square matrices.

Quadratic constraints

Mathematical ConstraintMOI FunctionMOI Set
$\frac{1}{2}x^TQx + a^Tx + b \ge 0$ScalarQuadraticFunctionGreaterThan
$\frac{1}{2}x^TQx + a^Tx + b \le 0$ScalarQuadraticFunctionLessThan
$\frac{1}{2}x^TQx + a^Tx + b = 0$ScalarQuadraticFunctionEqualTo
Bilinear matrix inequalityVectorQuadraticFunctionPositiveSemidefiniteCone...
Note

For more details on the internal format of the quadratic functions see ScalarQuadraticFunction or VectorQuadraticFunction.

Discrete and logical constraints

Mathematical ConstraintMOI FunctionMOI Set
$x_i \in \mathbb{Z}$VariableIndexInteger
$x_i \in \{0,1\}$VariableIndexZeroOne
$x_i \in \{0\} \cup [l,u]$VariableIndexSemicontinuous
$x_i \in \{0\} \cup \{l,l+1,\ldots,u-1,u\}$VariableIndexSemiinteger
At most one component of $x$ can be nonzeroVectorOfVariablesSOS1
At most two components of $x$ can be nonzero, and if so they must be adjacent componentsVectorOfVariablesSOS2
$y = 1 \implies a^T x \in S$VectorAffineFunctionIndicator

JuMP mapping

The following bullet points show examples of how JuMP constraints are translated into MOI function-set pairs:

  • @constraint(m, 2x + y <= 10) becomes ScalarAffineFunction-in-LessThan
  • @constraint(m, 2x + y >= 10) becomes ScalarAffineFunction-in-GreaterThan
  • @constraint(m, 2x + y == 10) becomes ScalarAffineFunction-in-EqualTo
  • @constraint(m, 0 <= 2x + y <= 10) becomes ScalarAffineFunction-in-Interval
  • @constraint(m, 2x + y in ArbitrarySet()) becomes ScalarAffineFunction-in-ArbitrarySet.

Variable bounds are handled in a similar fashion:

  • @variable(m, x <= 1) becomes VariableIndex-in-LessThan
  • @variable(m, x >= 1) becomes VariableIndex-in-GreaterThan

One notable difference is that a variable with an upper and lower bound is translated into two constraints, rather than an interval, that is:

  • @variable(m, 0 <= x <= 1) becomes VariableIndex-in-LessThan and VariableIndex-in-GreaterThan.
+"con_c"

Constraints by function-set pairs

Below is a list of common constraint types and how they are represented as function-set pairs in MOI. In the notation below, $x$ is a vector of decision variables, $x_i$ is a scalar decision variable, $\alpha, \beta$ are scalar constants, $a, b$ are constant vectors, A is a constant matrix and $\mathbb{R}_+$ (resp. $\mathbb{R}_-$) is the set of non-negative (resp. non-positive) real numbers.

Linear constraints

Mathematical ConstraintMOI FunctionMOI Set
$a^Tx \le \beta$ScalarAffineFunctionLessThan
$a^Tx \ge \alpha$ScalarAffineFunctionGreaterThan
$a^Tx = \beta$ScalarAffineFunctionEqualTo
$\alpha \le a^Tx \le \beta$ScalarAffineFunctionInterval
$x_i \le \beta$VariableIndexLessThan
$x_i \ge \alpha$VariableIndexGreaterThan
$x_i = \beta$VariableIndexEqualTo
$\alpha \le x_i \le \beta$VariableIndexInterval
$Ax + b \in \mathbb{R}_+^n$VectorAffineFunctionNonnegatives
$Ax + b \in \mathbb{R}_-^n$VectorAffineFunctionNonpositives
$Ax + b = 0$VectorAffineFunctionZeros

By convention, solvers are not expected to support nonzero constant terms in the ScalarAffineFunctions the first four rows of the preceding table because they are redundant with the parameters of the sets. For example, encode $2x + 1 \le 2$ as $2x \le 1$.

Constraints with VariableIndex in LessThan, GreaterThan, EqualTo, or Interval sets have a natural interpretation as variable bounds. As such, it is typically not natural to impose multiple lower- or upper-bounds on the same variable, and the solver interfaces will throw respectively LowerBoundAlreadySet or UpperBoundAlreadySet.

Moreover, adding two VariableIndex constraints on the same variable with the same set is impossible because they share the same index as it is the index of the variable, see ConstraintIndex.

It is natural, however, to impose upper- and lower-bounds separately as two different constraints on a single variable. The difference between imposing bounds by using a single Interval constraint and by using separate LessThan and GreaterThan constraints is that the latter will allow the solver to return separate dual multipliers for the two bounds, while the former will allow the solver to return only a single dual for the interval constraint.

Conic constraints

Mathematical ConstraintMOI FunctionMOI Set
$\lVert Ax + b\rVert_2 \le c^Tx + d$VectorAffineFunctionSecondOrderCone
$y \ge \lVert x \rVert_2$VectorOfVariablesSecondOrderCone
$2yz \ge \lVert x \rVert_2^2, y,z \ge 0$VectorOfVariablesRotatedSecondOrderCone
$(a_1^Tx + b_1,a_2^Tx + b_2,a_3^Tx + b_3) \in \mathcal{E}$VectorAffineFunctionExponentialCone
$A(x) \in \mathcal{S}_+$VectorAffineFunctionPositiveSemidefiniteConeTriangle
$B(x) \in \mathcal{S}_+$VectorAffineFunctionPositiveSemidefiniteConeSquare
$x \in \mathcal{S}_+$VectorOfVariablesPositiveSemidefiniteConeTriangle
$x \in \mathcal{S}_+$VectorOfVariablesPositiveSemidefiniteConeSquare

where $\mathcal{E}$ is the exponential cone (see ExponentialCone), $\mathcal{S}_+$ is the set of positive semidefinite symmetric matrices, $A$ is an affine map that outputs symmetric matrices and $B$ is an affine map that outputs square matrices.

Quadratic constraints

Mathematical ConstraintMOI FunctionMOI Set
$\frac{1}{2}x^TQx + a^Tx + b \ge 0$ScalarQuadraticFunctionGreaterThan
$\frac{1}{2}x^TQx + a^Tx + b \le 0$ScalarQuadraticFunctionLessThan
$\frac{1}{2}x^TQx + a^Tx + b = 0$ScalarQuadraticFunctionEqualTo
Bilinear matrix inequalityVectorQuadraticFunctionPositiveSemidefiniteCone...
Note

For more details on the internal format of the quadratic functions see ScalarQuadraticFunction or VectorQuadraticFunction.

Discrete and logical constraints

Mathematical ConstraintMOI FunctionMOI Set
$x_i \in \mathbb{Z}$VariableIndexInteger
$x_i \in \{0,1\}$VariableIndexZeroOne
$x_i \in \{0\} \cup [l,u]$VariableIndexSemicontinuous
$x_i \in \{0\} \cup \{l,l+1,\ldots,u-1,u\}$VariableIndexSemiinteger
At most one component of $x$ can be nonzeroVectorOfVariablesSOS1
At most two components of $x$ can be nonzero, and if so they must be adjacent componentsVectorOfVariablesSOS2
$y = 1 \implies a^T x \in S$VectorAffineFunctionIndicator

JuMP mapping

The following bullet points show examples of how JuMP constraints are translated into MOI function-set pairs:

  • @constraint(m, 2x + y <= 10) becomes ScalarAffineFunction-in-LessThan
  • @constraint(m, 2x + y >= 10) becomes ScalarAffineFunction-in-GreaterThan
  • @constraint(m, 2x + y == 10) becomes ScalarAffineFunction-in-EqualTo
  • @constraint(m, 0 <= 2x + y <= 10) becomes ScalarAffineFunction-in-Interval
  • @constraint(m, 2x + y in ArbitrarySet()) becomes ScalarAffineFunction-in-ArbitrarySet.

Variable bounds are handled in a similar fashion:

  • @variable(m, x <= 1) becomes VariableIndex-in-LessThan
  • @variable(m, x >= 1) becomes VariableIndex-in-GreaterThan

One notable difference is that a variable with an upper and lower bound is translated into two constraints, rather than an interval, that is:

  • @variable(m, 0 <= x <= 1) becomes VariableIndex-in-LessThan and VariableIndex-in-GreaterThan.
diff --git a/dev/manual/models/index.html b/dev/manual/models/index.html index 49a7eae940..65d70a9b03 100644 --- a/dev/manual/models/index.html +++ b/dev/manual/models/index.html @@ -1,2 +1,2 @@ -Models · MathOptInterface

Models

The most significant part of MOI is the definition of the model API that is used to specify an instance of an optimization problem (for example, by adding variables and constraints). Objects that implement the model API must inherit from the ModelLike abstract type.

Notably missing from the model API is the method to solve an optimization problem. ModelLike objects may store an instance (for example, in memory or backed by a file format) without being linked to a particular solver. In addition to the model API, MOI defines AbstractOptimizer and provides methods to solve the model and interact with solutions. See the Solutions section for more details.

Info

Throughout the rest of the manual, model is used as a generic ModelLike, and optimizer is used as a generic AbstractOptimizer.

Tip

MOI does not export functions, but for brevity we often omit qualifying names with the MOI module. Best practice is to have

import MathOptInterface as MOI

and prefix all MOI methods with MOI. in user code. If a name is also available in base Julia, we always explicitly use the module prefix, for example, with MOI.get.

Attributes

Attributes are properties of the model that can be queried and modified. These include constants such as the number of variables in a model NumberOfVariables), and properties of variables and constraints such as the name of a variable (VariableName).

There are four types of attributes:

Some attributes are values that can be queried by the user but not modified, while other attributes can be modified by the user.

All interactions with attributes occur through the get and set functions.

Consult the docstrings of each attribute for information on what it represents.

ModelLike API

The following attributes are available:

AbstractOptimizer API

The following attributes are available:

+Models · MathOptInterface

Models

The most significant part of MOI is the definition of the model API that is used to specify an instance of an optimization problem (for example, by adding variables and constraints). Objects that implement the model API must inherit from the ModelLike abstract type.

Notably missing from the model API is the method to solve an optimization problem. ModelLike objects may store an instance (for example, in memory or backed by a file format) without being linked to a particular solver. In addition to the model API, MOI defines AbstractOptimizer and provides methods to solve the model and interact with solutions. See the Solutions section for more details.

Info

Throughout the rest of the manual, model is used as a generic ModelLike, and optimizer is used as a generic AbstractOptimizer.

Tip

MOI does not export functions, but for brevity we often omit qualifying names with the MOI module. Best practice is to have

import MathOptInterface as MOI

and prefix all MOI methods with MOI. in user code. If a name is also available in base Julia, we always explicitly use the module prefix, for example, with MOI.get.

Attributes

Attributes are properties of the model that can be queried and modified. These include constants such as the number of variables in a model NumberOfVariables), and properties of variables and constraints such as the name of a variable (VariableName).

There are four types of attributes:

Some attributes are values that can be queried by the user but not modified, while other attributes can be modified by the user.

All interactions with attributes occur through the get and set functions.

Consult the docstrings of each attribute for information on what it represents.

ModelLike API

The following attributes are available:

AbstractOptimizer API

The following attributes are available:

diff --git a/dev/manual/modification/index.html b/dev/manual/modification/index.html index 339a9ea809..bfa14bcce4 100644 --- a/dev/manual/modification/index.html +++ b/dev/manual/modification/index.html @@ -148,4 +148,4 @@ ); julia> MOI.get(model, MOI.ConstraintFunction(), c) ≈ new_f -true +true diff --git a/dev/manual/solutions/index.html b/dev/manual/solutions/index.html index fed8c80b5c..c1898a9fab 100644 --- a/dev/manual/solutions/index.html +++ b/dev/manual/solutions/index.html @@ -32,4 +32,4 @@ end rethrow(err) # Something else went wrong. Rethrow the error end -end +end diff --git a/dev/manual/standard_form/index.html b/dev/manual/standard_form/index.html index 0ceb6db0c0..bffcfb7b1e 100644 --- a/dev/manual/standard_form/index.html +++ b/dev/manual/standard_form/index.html @@ -3,4 +3,4 @@ & \min_{x \in \mathbb{R}^n} & f_0(x) \\ & \;\;\text{s.t.} & f_i(x) & \in \mathcal{S}_i & i = 1 \ldots m -\end{align}\]

where:

  • the functions $f_0, f_1, \ldots, f_m$ are specified by AbstractFunction objects
  • the sets $\mathcal{S}_1, \ldots, \mathcal{S}_m$ are specified by AbstractSet objects
Tip

For more information on this standard form, read our paper.

MOI defines some commonly used functions and sets, but the interface is extensible to other sets recognized by the solver.

Functions

The function types implemented in MathOptInterface.jl are:

FunctionDescription
VariableIndex$x_j$, the projection onto a single coordinate defined by a variable index $j$.
VectorOfVariablesThe projection onto multiple coordinates (that is, extracting a sub-vector).
ScalarAffineFunction$a^T x + b$, where $a$ is a vector and $b$ scalar.
ScalarNonlinearFunction$f(x)$, where $f$ is a nonlinear function.
VectorAffineFunction$A x + b$, where $A$ is a matrix and $b$ is a vector.
ScalarQuadraticFunction$\frac{1}{2} x^T Q x + a^T x + b$, where $Q$ is a symmetric matrix, $a$ is a vector, and $b$ is a constant.
VectorQuadraticFunctionA vector of scalar-valued quadratic functions.
VectorNonlinearFunction$f(x)$, where $f$ is a vector-valued nonlinear function.

Extensions for nonlinear programming are present but not yet well documented.

One-dimensional sets

The one-dimensional set types implemented in MathOptInterface.jl are:

SetDescription
LessThan(u)$(-\infty, u]$
GreaterThan(l)$[l, \infty)$
EqualTo(v)$\{v\}$
Interval(l, u)$[l, u]$
Integer()$\mathbb{Z}$
ZeroOne()$\{ 0, 1 \}$
Semicontinuous(l, u)$\{ 0\} \cup [l, u]$
Semiinteger(l, u)$\{ 0\} \cup \{l,l+1,\ldots,u-1,u\}$

Vector cones

The vector-valued set types implemented in MathOptInterface.jl are:

SetDescription
Reals(d)$\mathbb{R}^{d}$
Zeros(d)$0^{d}$
Nonnegatives(d)$\{ x \in \mathbb{R}^{d} : x \ge 0 \}$
Nonpositives(d)$\{ x \in \mathbb{R}^{d} : x \le 0 \}$
SecondOrderCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \lVert x \rVert_2 \}$
RotatedSecondOrderCone(d)$\{ (t,u,x) \in \mathbb{R}^{d} : 2tu \ge \lVert x \rVert_2^2, t \ge 0,u \ge 0 \}$
ExponentialCone()$\{ (x,y,z) \in \mathbb{R}^3 : y \exp (x/y) \le z, y > 0 \}$
DualExponentialCone()$\{ (u,v,w) \in \mathbb{R}^3 : -u \exp (v/u) \le \exp(1) w, u < 0 \}$
GeometricMeanCone(d)$\{ (t,x) \in \mathbb{R}^{1+n} : x \ge 0, t \le \sqrt[n]{x_1 x_2 \cdots x_n} \}$ where $n$ is $d - 1$
PowerCone(α)$\{ (x,y,z) \in \mathbb{R}^3 : x^{\alpha} y^{1-\alpha} \ge |z|, x \ge 0,y \ge 0 \}$
DualPowerCone(α)$\{ (u,v,w) \in \mathbb{R}^3 : \left(\frac{u}{\alpha}\right)^{\alpha}\left(\frac{v}{1-\alpha}\right)^{1-\alpha} \ge |w|, u,v \ge 0 \}$
NormOneCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \sum_i \lvert x_i \rvert \}$
NormInfinityCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \max_i \lvert x_i \rvert \}$
RelativeEntropyCone(d)$\{ (u, v, w) \in \mathbb{R}^{d} : u \ge \sum_i w_i \log (\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}$
HyperRectangle(l, u)$\{x \in \bar{\mathbb{R}}^d: x_i \in [l_i, u_i] \forall i=1,\ldots,d\}$
NormCone(p, d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \left(\sum\limits_i \lvert x_i \rvert^p\right)^{\frac{1}{p}} \}$

Matrix cones

The matrix-valued set types implemented in MathOptInterface.jl are:

SetDescription
RootDetConeTriangle(d)$\{ (t,X) \in \mathbb{R}^{1+d(1+d)/2} : t \le \det(X)^{1/d}, X \mbox{ is the upper triangle of a PSD matrix} \}$
RootDetConeSquare(d)$\{ (t,X) \in \mathbb{R}^{1+d^2} : t \le \det(X)^{1/d}, X \mbox{ is a PSD matrix} \}$
PositiveSemidefiniteConeTriangle(d)$\{ X \in \mathbb{R}^{d(d+1)/2} : X \mbox{ is the upper triangle of a PSD matrix} \}$
PositiveSemidefiniteConeSquare(d)$\{ X \in \mathbb{R}^{d^2} : X \mbox{ is a PSD matrix} \}$
LogDetConeTriangle(d)$\{ (t,u,X) \in \mathbb{R}^{2+d(1+d)/2} : t \le u\log(\det(X/u)), X \mbox{ is the upper triangle of a PSD matrix}, u > 0 \}$
LogDetConeSquare(d)$\{ (t,u,X) \in \mathbb{R}^{2+d^2} : t \le u \log(\det(X/u)), X \mbox{ is a PSD matrix}, u > 0 \}$
NormSpectralCone(r, c)$\{ (t, X) \in \mathbb{R}^{1 + r \times c} : t \ge \sigma_1(X), X \mbox{ is a } r\times c\mbox{ matrix} \}$
NormNuclearCone(r, c)$\{ (t, X) \in \mathbb{R}^{1 + r \times c} : t \ge \sum_i \sigma_i(X), X \mbox{ is a } r\times c\mbox{ matrix} \}$
HermitianPositiveSemidefiniteConeTriangle(d)The cone of Hermitian positive semidefinite matrices, with
side_dimension rows and columns.
Scaled(S)The set S scaled so that Utilities.set_dot corresponds to LinearAlgebra.dot

Some of these cones can take two forms: XXXConeTriangle and XXXConeSquare.

In XXXConeTriangle sets, the matrix is assumed to be symmetric, and the elements are provided by a vector, in which the entries of the upper-right triangular part of the matrix are given column by column (or equivalently, the entries of the lower-left triangular part are given row by row).

In XXXConeSquare sets, the entries of the matrix are given column by column (or equivalently, row by row), and the matrix is constrained to be symmetric. As an example, given a 2-by-2 matrix of variables X and a one-dimensional variable t, we can specify a root-det constraint as [t, X11, X12, X22] ∈ RootDetConeTriangle or [t, X11, X12, X21, X22] ∈ RootDetConeSquare.

We provide both forms to enable flexibility for solvers who may natively support one or the other. Transformations between XXXConeTriangle and XXXConeSquare are handled by bridges, which removes the chance of conversion mistakes by users or solver developers.

Multi-dimensional sets with combinatorial structure

Other sets are vector-valued, with a particular combinatorial structure. Read their docstrings for more information on how to interpret them.

SetDescription
SOS1A Special Ordered Set (SOS) of Type I
SOS2A Special Ordered Set (SOS) of Type II
IndicatorA set to specify an indicator constraint
ComplementsA set to specify a mixed complementarity constraint
AllDifferentThe all_different global constraint
BinPackingThe bin_packing global constraint
CircuitThe circuit global constraint
CountAtLeastThe at_least global constraint
CountBelongsThe nvalue global constraint
CountDistinctThe distinct global constraint
CountGreaterThanThe count_gt global constraint
CumulativeThe cumulative global constraint
PathThe path global constraint
TableThe table global constraint
+\end{align}\]

where:

  • the functions $f_0, f_1, \ldots, f_m$ are specified by AbstractFunction objects
  • the sets $\mathcal{S}_1, \ldots, \mathcal{S}_m$ are specified by AbstractSet objects
Tip

For more information on this standard form, read our paper.

MOI defines some commonly used functions and sets, but the interface is extensible to other sets recognized by the solver.

Functions

The function types implemented in MathOptInterface.jl are:

FunctionDescription
VariableIndex$x_j$, the projection onto a single coordinate defined by a variable index $j$.
VectorOfVariablesThe projection onto multiple coordinates (that is, extracting a sub-vector).
ScalarAffineFunction$a^T x + b$, where $a$ is a vector and $b$ scalar.
ScalarNonlinearFunction$f(x)$, where $f$ is a nonlinear function.
VectorAffineFunction$A x + b$, where $A$ is a matrix and $b$ is a vector.
ScalarQuadraticFunction$\frac{1}{2} x^T Q x + a^T x + b$, where $Q$ is a symmetric matrix, $a$ is a vector, and $b$ is a constant.
VectorQuadraticFunctionA vector of scalar-valued quadratic functions.
VectorNonlinearFunction$f(x)$, where $f$ is a vector-valued nonlinear function.

Extensions for nonlinear programming are present but not yet well documented.

One-dimensional sets

The one-dimensional set types implemented in MathOptInterface.jl are:

SetDescription
LessThan(u)$(-\infty, u]$
GreaterThan(l)$[l, \infty)$
EqualTo(v)$\{v\}$
Interval(l, u)$[l, u]$
Integer()$\mathbb{Z}$
ZeroOne()$\{ 0, 1 \}$
Semicontinuous(l, u)$\{ 0\} \cup [l, u]$
Semiinteger(l, u)$\{ 0\} \cup \{l,l+1,\ldots,u-1,u\}$

Vector cones

The vector-valued set types implemented in MathOptInterface.jl are:

SetDescription
Reals(d)$\mathbb{R}^{d}$
Zeros(d)$0^{d}$
Nonnegatives(d)$\{ x \in \mathbb{R}^{d} : x \ge 0 \}$
Nonpositives(d)$\{ x \in \mathbb{R}^{d} : x \le 0 \}$
SecondOrderCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \lVert x \rVert_2 \}$
RotatedSecondOrderCone(d)$\{ (t,u,x) \in \mathbb{R}^{d} : 2tu \ge \lVert x \rVert_2^2, t \ge 0,u \ge 0 \}$
ExponentialCone()$\{ (x,y,z) \in \mathbb{R}^3 : y \exp (x/y) \le z, y > 0 \}$
DualExponentialCone()$\{ (u,v,w) \in \mathbb{R}^3 : -u \exp (v/u) \le \exp(1) w, u < 0 \}$
GeometricMeanCone(d)$\{ (t,x) \in \mathbb{R}^{1+n} : x \ge 0, t \le \sqrt[n]{x_1 x_2 \cdots x_n} \}$ where $n$ is $d - 1$
PowerCone(α)$\{ (x,y,z) \in \mathbb{R}^3 : x^{\alpha} y^{1-\alpha} \ge |z|, x \ge 0,y \ge 0 \}$
DualPowerCone(α)$\{ (u,v,w) \in \mathbb{R}^3 : \left(\frac{u}{\alpha}\right)^{\alpha}\left(\frac{v}{1-\alpha}\right)^{1-\alpha} \ge |w|, u,v \ge 0 \}$
NormOneCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \sum_i \lvert x_i \rvert \}$
NormInfinityCone(d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \max_i \lvert x_i \rvert \}$
RelativeEntropyCone(d)$\{ (u, v, w) \in \mathbb{R}^{d} : u \ge \sum_i w_i \log (\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}$
HyperRectangle(l, u)$\{x \in \bar{\mathbb{R}}^d: x_i \in [l_i, u_i] \forall i=1,\ldots,d\}$
NormCone(p, d)$\{ (t,x) \in \mathbb{R}^{d} : t \ge \left(\sum\limits_i \lvert x_i \rvert^p\right)^{\frac{1}{p}} \}$

Matrix cones

The matrix-valued set types implemented in MathOptInterface.jl are:

SetDescription
RootDetConeTriangle(d)$\{ (t,X) \in \mathbb{R}^{1+d(1+d)/2} : t \le \det(X)^{1/d}, X \mbox{ is the upper triangle of a PSD matrix} \}$
RootDetConeSquare(d)$\{ (t,X) \in \mathbb{R}^{1+d^2} : t \le \det(X)^{1/d}, X \mbox{ is a PSD matrix} \}$
PositiveSemidefiniteConeTriangle(d)$\{ X \in \mathbb{R}^{d(d+1)/2} : X \mbox{ is the upper triangle of a PSD matrix} \}$
PositiveSemidefiniteConeSquare(d)$\{ X \in \mathbb{R}^{d^2} : X \mbox{ is a PSD matrix} \}$
LogDetConeTriangle(d)$\{ (t,u,X) \in \mathbb{R}^{2+d(1+d)/2} : t \le u\log(\det(X/u)), X \mbox{ is the upper triangle of a PSD matrix}, u > 0 \}$
LogDetConeSquare(d)$\{ (t,u,X) \in \mathbb{R}^{2+d^2} : t \le u \log(\det(X/u)), X \mbox{ is a PSD matrix}, u > 0 \}$
NormSpectralCone(r, c)$\{ (t, X) \in \mathbb{R}^{1 + r \times c} : t \ge \sigma_1(X), X \mbox{ is a } r\times c\mbox{ matrix} \}$
NormNuclearCone(r, c)$\{ (t, X) \in \mathbb{R}^{1 + r \times c} : t \ge \sum_i \sigma_i(X), X \mbox{ is a } r\times c\mbox{ matrix} \}$
HermitianPositiveSemidefiniteConeTriangle(d)The cone of Hermitian positive semidefinite matrices, with
side_dimension rows and columns.
Scaled(S)The set S scaled so that Utilities.set_dot corresponds to LinearAlgebra.dot

Some of these cones can take two forms: XXXConeTriangle and XXXConeSquare.

In XXXConeTriangle sets, the matrix is assumed to be symmetric, and the elements are provided by a vector, in which the entries of the upper-right triangular part of the matrix are given column by column (or equivalently, the entries of the lower-left triangular part are given row by row).

In XXXConeSquare sets, the entries of the matrix are given column by column (or equivalently, row by row), and the matrix is constrained to be symmetric. As an example, given a 2-by-2 matrix of variables X and a one-dimensional variable t, we can specify a root-det constraint as [t, X11, X12, X22] ∈ RootDetConeTriangle or [t, X11, X12, X21, X22] ∈ RootDetConeSquare.

We provide both forms to enable flexibility for solvers who may natively support one or the other. Transformations between XXXConeTriangle and XXXConeSquare are handled by bridges, which removes the chance of conversion mistakes by users or solver developers.

Multi-dimensional sets with combinatorial structure

Other sets are vector-valued, with a particular combinatorial structure. Read their docstrings for more information on how to interpret them.

SetDescription
SOS1A Special Ordered Set (SOS) of Type I
SOS2A Special Ordered Set (SOS) of Type II
IndicatorA set to specify an indicator constraint
ComplementsA set to specify a mixed complementarity constraint
AllDifferentThe all_different global constraint
BinPackingThe bin_packing global constraint
CircuitThe circuit global constraint
CountAtLeastThe at_least global constraint
CountBelongsThe nvalue global constraint
CountDistinctThe distinct global constraint
CountGreaterThanThe count_gt global constraint
CumulativeThe cumulative global constraint
PathThe path global constraint
TableThe table global constraint
diff --git a/dev/manual/variables/index.html b/dev/manual/variables/index.html index 9daf4d4a40..166059971d 100644 --- a/dev/manual/variables/index.html +++ b/dev/manual/variables/index.html @@ -10,4 +10,4 @@ false
Warning

Not all ModelLike models support deleting variables. A DeleteNotAllowed error is thrown if this is not supported.

Variable attributes

The following attributes are available for variables:

Get and set these attributes using get and set.

julia> MOI.set(model, MOI.VariableName(), x, "var_x")
 
 julia> MOI.get(model, MOI.VariableName(), x)
-"var_x"
+"var_x" diff --git a/dev/reference/callbacks/index.html b/dev/reference/callbacks/index.html index 030b387ee4..14d3f91e56 100644 --- a/dev/reference/callbacks/index.html +++ b/dev/reference/callbacks/index.html @@ -1,9 +1,9 @@ -Callbacks · MathOptInterface

Callbacks

MathOptInterface.AbstractCallbackType
abstract type AbstractCallback <: AbstractModelAttribute end

Abstract type for a model attribute representing a callback function. The value set to subtypes of AbstractCallback is a function that may be called during optimize!. As optimize! is in progress, the result attributes (that is, the attributes attr such that is_set_by_optimize(attr)) may not be accessible from the callback, hence trying to get result attributes might throw a OptimizeInProgress error.

At most one callback of each type can be registered. If an optimizer already has a function for a callback type, and the user registers a new function, then the old one is replaced.

The value of the attribute should be a function taking only one argument, commonly called callback_data, that can be used for instance in LazyConstraintCallback, HeuristicCallback and UserCutCallback.

source
MathOptInterface.submitFunction
submit(
+Callbacks · MathOptInterface

Callbacks

MathOptInterface.AbstractCallbackType
abstract type AbstractCallback <: AbstractModelAttribute end

Abstract type for a model attribute representing a callback function. The value set to subtypes of AbstractCallback is a function that may be called during optimize!. As optimize! is in progress, the result attributes (that is, the attributes attr such that is_set_by_optimize(attr)) may not be accessible from the callback, hence trying to get result attributes might throw a OptimizeInProgress error.

At most one callback of each type can be registered. If an optimizer already has a function for a callback type, and the user registers a new function, then the old one is replaced.

The value of the attribute should be a function taking only one argument, commonly called callback_data, that can be used for instance in LazyConstraintCallback, HeuristicCallback and UserCutCallback.

source
MathOptInterface.submitFunction
submit(
     optimizer::AbstractOptimizer,
     sub::AbstractSubmittable,
     values...,
-)::Nothing

Submit values to the submittable sub of the optimizer optimizer.

An UnsupportedSubmittable error is thrown if model does not support the attribute attr (see supports) and a SubmitNotAllowed error is thrown if it supports the submittable sub but it cannot be submitted.

source

Attributes

MathOptInterface.CallbackNodeStatusCodeType
CallbackNodeStatusCode

An Enum of possible return values from calling get with CallbackNodeStatus.

Values

Possible values are:

source

Lazy constraints

MathOptInterface.LazyConstraintCallbackType
LazyConstraintCallback() <: AbstractCallback

The callback can be used to reduce the feasible set given the current primal solution by submitting a LazyConstraint. For instance, it may be called at an incumbent of a mixed-integer problem. Note that there is no guarantee that the callback is called at every feasible primal solution.

The current primal solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
+)::Nothing

Submit values to the submittable sub of the optimizer optimizer.

An UnsupportedSubmittable error is thrown if model does not support the attribute attr (see supports) and a SubmitNotAllowed error is thrown if it supports the submittable sub but it cannot be submitted.

source

Attributes

MathOptInterface.CallbackNodeStatusCodeType
CallbackNodeStatusCode

An Enum of possible return values from calling get with CallbackNodeStatus.

Values

Possible values are:

source

Lazy constraints

MathOptInterface.LazyConstraintCallbackType
LazyConstraintCallback() <: AbstractCallback

The callback can be used to reduce the feasible set given the current primal solution by submitting a LazyConstraint. For instance, it may be called at an incumbent of a mixed-integer problem. Note that there is no guarantee that the callback is called at every feasible primal solution.

The current primal solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
 MOI.set(optimizer, MOI.LazyConstraintCallback(), callback_data -> begin
     sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
     if # should add a lazy constraint
@@ -11,9 +11,9 @@
         set = # computes set
         MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)
     end
-end)
source
MathOptInterface.LazyConstraintType
LazyConstraint(callback_data)

Lazy constraint func-in-set submitted as func, set. The optimal solution returned by VariablePrimal will satisfy all lazy constraints that have been submitted.

This can be submitted only from the LazyConstraintCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the feasible solution callback.

Example

Suppose x and y are VariableIndexs of optimizer. To add a LazyConstraint for 2x + 3y <= 1, write

func = 2.0x + 3.0y
+end)
source
MathOptInterface.LazyConstraintType
LazyConstraint(callback_data)

Lazy constraint func-in-set submitted as func, set. The optimal solution returned by VariablePrimal will satisfy all lazy constraints that have been submitted.

This can be submitted only from the LazyConstraintCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the feasible solution callback.

Example

Suppose x and y are VariableIndexs of optimizer. To add a LazyConstraint for 2x + 3y <= 1, write

func = 2.0x + 3.0y
 set = MOI.LessThan(1.0)
-MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)

inside a LazyConstraintCallback of data callback_data.

source

User cuts

MathOptInterface.UserCutCallbackType
UserCutCallback() <: AbstractCallback

The callback can be used to submit UserCut given the current primal solution. For instance, it may be called at fractional (that is, non-integer) nodes in the branch and bound tree of a mixed-integer problem. Note that there is not guarantee that the callback is called everytime the solver has an infeasible solution.

The infeasible solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
+MOI.submit(optimizer, MOI.LazyConstraint(callback_data), func, set)

inside a LazyConstraintCallback of data callback_data.

source

User cuts

MathOptInterface.UserCutCallbackType
UserCutCallback() <: AbstractCallback

The callback can be used to submit UserCut given the current primal solution. For instance, it may be called at fractional (that is, non-integer) nodes in the branch and bound tree of a mixed-integer problem. Note that there is not guarantee that the callback is called everytime the solver has an infeasible solution.

The infeasible solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
 MOI.set(optimizer, MOI.UserCutCallback(), callback_data -> begin
     sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
     if # can find a user cut
@@ -21,7 +21,7 @@
         set = # computes set
         MOI.submit(optimizer, MOI.UserCut(callback_data), func, set)
     end
-end
source
MathOptInterface.UserCutType
UserCut(callback_data)

Constraint func-to-set suggested to help the solver detect the solution given by CallbackVariablePrimal as infeasible. The cut is submitted as func, set. Typically CallbackVariablePrimal will violate integrality constraints, and a cut would be of the form ScalarAffineFunction-in-LessThan or ScalarAffineFunction-in-GreaterThan. Note that, as opposed to LazyConstraint, the provided constraint cannot modify the feasible set, the constraint should be redundant, for example, it may be a consequence of affine and integrality constraints.

This can be submitted only from the UserCutCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the infeasible solution callback.

Note that the solver may silently ignore the provided constraint.

source

Heuristic solutions

MathOptInterface.HeuristicCallbackType
HeuristicCallback() <: AbstractCallback

The callback can be used to submit HeuristicSolution given the current primal solution. For example, it may be called at fractional (that is, non-integer) nodes in the branch and bound tree of a mixed-integer problem. Note that there is no guarantee that the callback is called every time the solver has an infeasible solution.

The current primal solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
+end
source
MathOptInterface.UserCutType
UserCut(callback_data)

Constraint func-to-set suggested to help the solver detect the solution given by CallbackVariablePrimal as infeasible. The cut is submitted as func, set. Typically CallbackVariablePrimal will violate integrality constraints, and a cut would be of the form ScalarAffineFunction-in-LessThan or ScalarAffineFunction-in-GreaterThan. Note that, as opposed to LazyConstraint, the provided constraint cannot modify the feasible set, the constraint should be redundant, for example, it may be a consequence of affine and integrality constraints.

This can be submitted only from the UserCutCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the infeasible solution callback.

Note that the solver may silently ignore the provided constraint.

source

Heuristic solutions

MathOptInterface.HeuristicCallbackType
HeuristicCallback() <: AbstractCallback

The callback can be used to submit HeuristicSolution given the current primal solution. For example, it may be called at fractional (that is, non-integer) nodes in the branch and bound tree of a mixed-integer problem. Note that there is no guarantee that the callback is called every time the solver has an infeasible solution.

The current primal solution is accessed through CallbackVariablePrimal. Trying to access other result attributes will throw OptimizeInProgress as discussed in AbstractCallback.

Example

x = MOI.add_variables(optimizer, 8)
 MOI.set(optimizer, MOI.HeuristicCallback(), callback_data -> begin
     sol = MOI.get(optimizer, MOI.CallbackVariablePrimal(callback_data), x)
     if # can find a heuristic solution
@@ -29,4 +29,4 @@
         MOI.submit(optimizer, MOI.HeuristicSolution(callback_data), x,
                    values)
     end
-end
source
MathOptInterface.HeuristicSolutionType
HeuristicSolution(callback_data)

Heuristically obtained feasible solution. The solution is submitted as variables, values where values[i] gives the value of variables[i], similarly to set. The submit call returns a HeuristicSolutionStatus indicating whether the provided solution was accepted or rejected.

This can be submitted only from the HeuristicCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the heuristic callback.

Some solvers require a complete solution, others only partial solutions.

source
+end
source
MathOptInterface.HeuristicSolutionType
HeuristicSolution(callback_data)

Heuristically obtained feasible solution. The solution is submitted as variables, values where values[i] gives the value of variables[i], similarly to set. The submit call returns a HeuristicSolutionStatus indicating whether the provided solution was accepted or rejected.

This can be submitted only from the HeuristicCallback. The field callback_data is a solver-specific callback type that is passed as the argument to the heuristic callback.

Some solvers require a complete solution, others only partial solutions.

source
diff --git a/dev/reference/constraints/index.html b/dev/reference/constraints/index.html index d14102ac8a..b503db2fe8 100644 --- a/dev/reference/constraints/index.html +++ b/dev/reference/constraints/index.html @@ -1,6 +1,6 @@ -Constraints · MathOptInterface

Constraints

Types

MathOptInterface.ConstraintIndexType
ConstraintIndex{F,S}

A type-safe wrapper for Int64 for use in referencing F-in-S constraints in a model.

The parameter F is the type of the function in the constraint, and the parameter S is the type of set in the constraint.

To allow for deletion, indices need not be consecutive.

Indices within a constraint type (that is, F-in-S) must be unique, but non-unique indices across different constraint types are allowed.

If F is VariableIndex then the index is equal to the index of the variable. That is for an index::ConstraintIndex{VariableIndex}, we always have

index.value == MOI.get(model, MOI.ConstraintFunction(), index).value
source

Functions

MathOptInterface.is_validMethod
is_valid(model::ModelLike, index::Index)::Bool

Return a Bool indicating whether this index refers to a valid object in the model model.

source
MathOptInterface.add_constraintFunction
MOI.add_constraint(map::Map, vi::MOI.VariableIndex, set::MOI.AbstractScalarSet)

Record that a constraint vi-in-set is added and throws if a lower or upper bound is set by this constraint and such bound has already been set for vi.

source
add_constraint(model::ModelLike, func::F, set::S)::ConstraintIndex{F,S} where {F,S}

Add the constraint $f(x) \in \mathcal{S}$ where $f$ is defined by func, and $\mathcal{S}$ is defined by set.

add_constraint(model::ModelLike, v::VariableIndex, set::S)::ConstraintIndex{VariableIndex,S} where {S}
-add_constraint(model::ModelLike, vec::Vector{VariableIndex}, set::S)::ConstraintIndex{VectorOfVariables,S} where {S}

Add the constraint $v \in \mathcal{S}$ where $v$ is the variable (or vector of variables) referenced by v and $\mathcal{S}$ is defined by set.

source
MathOptInterface.add_constraintsFunction
add_constraints(model::ModelLike, funcs::Vector{F}, sets::Vector{S})::Vector{ConstraintIndex{F,S}} where {F,S}

Add the set of constraints specified by each function-set pair in funcs and sets. F and S should be concrete types. This call is equivalent to add_constraint.(model, funcs, sets) but may be more efficient.

source
MathOptInterface.transformFunction
transform(
+Constraints · MathOptInterface

Constraints

Types

MathOptInterface.ConstraintIndexType
ConstraintIndex{F,S}

A type-safe wrapper for Int64 for use in referencing F-in-S constraints in a model.

The parameter F is the type of the function in the constraint, and the parameter S is the type of set in the constraint.

To allow for deletion, indices need not be consecutive.

Indices within a constraint type (that is, F-in-S) must be unique, but non-unique indices across different constraint types are allowed.

If F is VariableIndex then the index is equal to the index of the variable. That is for an index::ConstraintIndex{VariableIndex}, we always have

index.value == MOI.get(model, MOI.ConstraintFunction(), index).value
source

Functions

MathOptInterface.is_validMethod
is_valid(model::ModelLike, index::Index)::Bool

Return a Bool indicating whether this index refers to a valid object in the model model.

source
MathOptInterface.add_constraintFunction
MOI.add_constraint(map::Map, vi::MOI.VariableIndex, set::MOI.AbstractScalarSet)

Record that a constraint vi-in-set is added and throws if a lower or upper bound is set by this constraint and such bound has already been set for vi.

source
add_constraint(model::ModelLike, func::F, set::S)::ConstraintIndex{F,S} where {F,S}

Add the constraint $f(x) \in \mathcal{S}$ where $f$ is defined by func, and $\mathcal{S}$ is defined by set.

add_constraint(model::ModelLike, v::VariableIndex, set::S)::ConstraintIndex{VariableIndex,S} where {S}
+add_constraint(model::ModelLike, vec::Vector{VariableIndex}, set::S)::ConstraintIndex{VectorOfVariables,S} where {S}

Add the constraint $v \in \mathcal{S}$ where $v$ is the variable (or vector of variables) referenced by v and $\mathcal{S}$ is defined by set.

source
MathOptInterface.add_constraintsFunction
add_constraints(model::ModelLike, funcs::Vector{F}, sets::Vector{S})::Vector{ConstraintIndex{F,S}} where {F,S}

Add the set of constraints specified by each function-set pair in funcs and sets. F and S should be concrete types. This call is equivalent to add_constraint.(model, funcs, sets) but may be more efficient.

source
MathOptInterface.transformFunction
transform(
     model::ModelLike,
     c::ConstraintIndex{F,S1},
     newset::S2,
@@ -32,12 +32,12 @@
  0.0 + 1.0 v[1] >= 0.0
 
 julia> MOI.is_valid(model, c)
-false
source
MathOptInterface.supports_constraintFunction
MOI.supports_constraint(
     BT::Type{<:AbstractBridge},
     F::Type{<:MOI.AbstractFunction},
     S::Type{<:MOI.AbstractSet},
-)::Bool

Return a Bool indicating whether the bridges of type BT support bridging F-in-S constraints.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method for constraint types that the bridge implements.
source
supports_constraint(
+)::Bool

Return a Bool indicating whether the bridges of type BT support bridging F-in-S constraints.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method for constraint types that the bridge implements.
source
supports_constraint(
     model::ModelLike,
     ::Type{F},
     ::Type{S},
-)::Bool where {F<:AbstractFunction,S<:AbstractSet}

Return a Bool indicating whether model supports F-in-S constraints, that is, copy_to(model, src) does not throw UnsupportedConstraint when src contains F-in-S constraints. If F-in-S constraints are only not supported in specific circumstances, for example, F-in-S constraints cannot be combined with another type of constraint, it should still return true.

source

Attributes

MathOptInterface.ConstraintNameType
ConstraintName()

A constraint attribute for a string identifying the constraint.

It is valid for constraints variables to have the same name; however, constraints with duplicate names cannot be looked up using get, regardless of whether they have the same F-in-S type.

ConstraintName has a default value of "" if not set.

Notes

You should not implement ConstraintName for VariableIndex constraints.

source
MathOptInterface.ConstraintPrimalType
ConstraintPrimal(result_index::Int = 1)

A constraint attribute for the assignment to some constraint's primal value in result result_index.

If the constraint is f(x) in S, then in most cases the ConstraintPrimal is the value of f, evaluated at the corresponding VariablePrimal solution.

However, some conic solvers reformulate b - Ax in S to s = b - Ax, s in S. These solvers may return the value of s for ConstraintPrimal, rather than b - Ax. (Although these are constrained by an equality constraint, due to numerical tolerances they may not be identical.)

If the solver does not have a primal value for the constraint because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ConstraintPrimal attribute.

If result_index is omitted, it is 1 by default. See ResultCount for information on how the results are ordered.

source
MathOptInterface.ConstraintDualType
ConstraintDual(result_index::Int = 1)

A constraint attribute for the assignment to some constraint's dual value in result result_index. If result_index is omitted, it is 1 by default.

If the solver does not have a dual value for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a primal solution is available), the result is undefined. Users should first check DualStatus before accessing the ConstraintDual attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.ConstraintBasisStatusType
ConstraintBasisStatus(result_index::Int = 1)

A constraint attribute for the BasisStatusCode of some constraint in result result_index, with respect to an available optimal solution basis. If result_index is omitted, it is 1 by default.

If the solver does not have a basis status for the constraint because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ConstraintBasisStatus attribute.

See ResultCount for information on how the results are ordered.

Notes

For the basis status of a variable, query VariableBasisStatus.

ConstraintBasisStatus does not apply to VariableIndex constraints. You can infer the basis status of a VariableIndex constraint by looking at the result of VariableBasisStatus.

source
MathOptInterface.ConstraintFunctionType
ConstraintFunction()

A constraint attribute for the AbstractFunction object used to define the constraint.

It is guaranteed to be equivalent but not necessarily identical to the function provided by the user.

source
MathOptInterface.CanonicalConstraintFunctionType
CanonicalConstraintFunction()

A constraint attribute for a canonical representation of the AbstractFunction object used to define the constraint.

Getting this attribute is guaranteed to return a function that is equivalent but not necessarily identical to the function provided by the user.

By default, MOI.get(model, MOI.CanonicalConstraintFunction(), ci) fallbacks to MOI.Utilities.canonical(MOI.get(model, MOI.ConstraintFunction(), ci)). However, if model knows that the constraint function is canonical then it can implement a specialized method that directly return the function without calling Utilities.canonical. Therefore, the value returned cannot be assumed to be a copy of the function stored in model. Moreover, Utilities.Model checks with Utilities.is_canonical whether the function stored internally is already canonical and if it's the case, then it returns the function stored internally instead of a copy.

source
MathOptInterface.BasisStatusCodeType
BasisStatusCode

An Enum of possible values for the ConstraintBasisStatus and VariableBasisStatus attributes, explaining the status of a given element with respect to an optimal solution basis.

Notes

  • NONBASIC_AT_LOWER and NONBASIC_AT_UPPER should be used only for constraints with the Interval set. In this case, they are necessary to distinguish which side of the constraint is active. One-sided constraints (for example, LessThan and GreaterThan) should use NONBASIC instead of the NONBASIC_AT_* values. This restriction does not apply to VariableBasisStatus, which should return NONBASIC_AT_* regardless of whether the alternative bound exists.

  • In linear programs, SUPER_BASIC occurs when a variable with no bounds is not in the basis.

Values

Possible values are:

source
+)::Bool where {F<:AbstractFunction,S<:AbstractSet}

Return a Bool indicating whether model supports F-in-S constraints, that is, copy_to(model, src) does not throw UnsupportedConstraint when src contains F-in-S constraints. If F-in-S constraints are only not supported in specific circumstances, for example, F-in-S constraints cannot be combined with another type of constraint, it should still return true.

source

Attributes

MathOptInterface.ConstraintNameType
ConstraintName()

A constraint attribute for a string identifying the constraint.

It is valid for constraints variables to have the same name; however, constraints with duplicate names cannot be looked up using get, regardless of whether they have the same F-in-S type.

ConstraintName has a default value of "" if not set.

Notes

You should not implement ConstraintName for VariableIndex constraints.

source
MathOptInterface.ConstraintPrimalType
ConstraintPrimal(result_index::Int = 1)

A constraint attribute for the assignment to some constraint's primal value in result result_index.

If the constraint is f(x) in S, then in most cases the ConstraintPrimal is the value of f, evaluated at the corresponding VariablePrimal solution.

However, some conic solvers reformulate b - Ax in S to s = b - Ax, s in S. These solvers may return the value of s for ConstraintPrimal, rather than b - Ax. (Although these are constrained by an equality constraint, due to numerical tolerances they may not be identical.)

If the solver does not have a primal value for the constraint because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ConstraintPrimal attribute.

If result_index is omitted, it is 1 by default. See ResultCount for information on how the results are ordered.

source
MathOptInterface.ConstraintDualType
ConstraintDual(result_index::Int = 1)

A constraint attribute for the assignment to some constraint's dual value in result result_index. If result_index is omitted, it is 1 by default.

If the solver does not have a dual value for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a primal solution is available), the result is undefined. Users should first check DualStatus before accessing the ConstraintDual attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.ConstraintBasisStatusType
ConstraintBasisStatus(result_index::Int = 1)

A constraint attribute for the BasisStatusCode of some constraint in result result_index, with respect to an available optimal solution basis. If result_index is omitted, it is 1 by default.

If the solver does not have a basis status for the constraint because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ConstraintBasisStatus attribute.

See ResultCount for information on how the results are ordered.

Notes

For the basis status of a variable, query VariableBasisStatus.

ConstraintBasisStatus does not apply to VariableIndex constraints. You can infer the basis status of a VariableIndex constraint by looking at the result of VariableBasisStatus.

source
MathOptInterface.ConstraintFunctionType
ConstraintFunction()

A constraint attribute for the AbstractFunction object used to define the constraint.

It is guaranteed to be equivalent but not necessarily identical to the function provided by the user.

source
MathOptInterface.CanonicalConstraintFunctionType
CanonicalConstraintFunction()

A constraint attribute for a canonical representation of the AbstractFunction object used to define the constraint.

Getting this attribute is guaranteed to return a function that is equivalent but not necessarily identical to the function provided by the user.

By default, MOI.get(model, MOI.CanonicalConstraintFunction(), ci) fallbacks to MOI.Utilities.canonical(MOI.get(model, MOI.ConstraintFunction(), ci)). However, if model knows that the constraint function is canonical then it can implement a specialized method that directly return the function without calling Utilities.canonical. Therefore, the value returned cannot be assumed to be a copy of the function stored in model. Moreover, Utilities.Model checks with Utilities.is_canonical whether the function stored internally is already canonical and if it's the case, then it returns the function stored internally instead of a copy.

source
MathOptInterface.BasisStatusCodeType
BasisStatusCode

An Enum of possible values for the ConstraintBasisStatus and VariableBasisStatus attributes, explaining the status of a given element with respect to an optimal solution basis.

Notes

  • NONBASIC_AT_LOWER and NONBASIC_AT_UPPER should be used only for constraints with the Interval set. In this case, they are necessary to distinguish which side of the constraint is active. One-sided constraints (for example, LessThan and GreaterThan) should use NONBASIC instead of the NONBASIC_AT_* values. This restriction does not apply to VariableBasisStatus, which should return NONBASIC_AT_* regardless of whether the alternative bound exists.

  • In linear programs, SUPER_BASIC occurs when a variable with no bounds is not in the basis.

Values

Possible values are:

source
diff --git a/dev/reference/errors/index.html b/dev/reference/errors/index.html index 94ff0becc2..0554c7d4e7 100644 --- a/dev/reference/errors/index.html +++ b/dev/reference/errors/index.html @@ -1,25 +1,25 @@ Errors · MathOptInterface

Errors

When an MOI call fails on a model, precise errors should be thrown when possible instead of simply calling error with a message. The docstrings for the respective methods describe the errors that the implementation should throw in certain situations. This error-reporting system allows code to distinguish between internal errors (that should be shown to the user) and unsupported operations which may have automatic workarounds.

When an invalid index is used in an MOI call, an InvalidIndex is thrown:

When an invalid result index is used to retrieve an attribute, a ResultIndexBoundsError is thrown:

When an invalid result index is used to retrieve an attribute, a ResultIndexBoundsError is thrown:

MathOptInterface.ResultIndexBoundsErrorType
struct ResultIndexBoundsError{AttrType} <: Exception
     attr::AttrType
     result_count::Int
-end

An error indicating that the requested attribute attr could not be retrieved, because the solver returned too few results compared to what was requested. For instance, the user tries to retrieve VariablePrimal(2) when only one solution is available, or when the model is infeasible and has no solution.

See also: check_result_index_bounds.

source

As discussed in JuMP mapping, for scalar constraint with a nonzero function constant, a ScalarFunctionConstantNotZero exception may be thrown:

MathOptInterface.ScalarFunctionConstantNotZeroType
struct ScalarFunctionConstantNotZero{T, F, S} <: Exception
+end

An error indicating that the requested attribute attr could not be retrieved, because the solver returned too few results compared to what was requested. For instance, the user tries to retrieve VariablePrimal(2) when only one solution is available, or when the model is infeasible and has no solution.

See also: check_result_index_bounds.

source

As discussed in JuMP mapping, for scalar constraint with a nonzero function constant, a ScalarFunctionConstantNotZero exception may be thrown:

Some VariableIndex constraints cannot be combined on the same variable:

As discussed in AbstractCallback, trying to get attributes inside a callback may throw:

MathOptInterface.OptimizeInProgressType
struct OptimizeInProgress{AttrType<:AnyAttribute} <: Exception
+end

An error indicating that the constant part of the function in the constraint F-in-S is nonzero.

source

Some VariableIndex constraints cannot be combined on the same variable:

As discussed in AbstractCallback, trying to get attributes inside a callback may throw:

MathOptInterface.OptimizeInProgressType
struct OptimizeInProgress{AttrType<:AnyAttribute} <: Exception
     attr::AttrType
-end

Error thrown from optimizer when MOI.get(optimizer, attr) is called inside an AbstractCallback while it is only defined once optimize! has completed. This can only happen when is_set_by_optimize(attr) is true.

source

Trying to submit the wrong type of AbstractSubmittable inside an AbstractCallback (for example, a UserCut inside a LazyConstraintCallback) will throw:

Trying to submit the wrong type of AbstractSubmittable inside an AbstractCallback (for example, a UserCut inside a LazyConstraintCallback) will throw:

The rest of the errors defined in MOI fall in two categories represented by the following two abstract types:

MathOptInterface.NotAllowedErrorType
NotAllowedError <: Exception

Abstract type for error thrown when an operation is supported but cannot be applied in the current state of the model.

source

The different UnsupportedError and NotAllowedError are the following errors:

The rest of the errors defined in MOI fall in two categories represented by the following two abstract types:

MathOptInterface.NotAllowedErrorType
NotAllowedError <: Exception

Abstract type for error thrown when an operation is supported but cannot be applied in the current state of the model.

source

The different UnsupportedError and NotAllowedError are the following errors:

MathOptInterface.UnsupportedAttributeType
struct UnsupportedAttribute{AttrType} <: UnsupportedError
     attr::AttrType
     message::String
-end

An error indicating that the attribute attr is not supported by the model, that is, that supports returns false.

source
MathOptInterface.SetAttributeNotAllowedType
struct SetAttributeNotAllowed{AttrType} <: NotAllowedError
     attr::AttrType
     message::String
-end

An error indicating that the attribute attr is supported (see supports) but cannot be set for some reason (see the error string).

source
MathOptInterface.AddVariableNotAllowedType
struct AddVariableNotAllowed <: NotAllowedError
     message::String # Human-friendly explanation why the attribute cannot be set
-end

An error indicating that variables cannot be added to the model.

source
MathOptInterface.UnsupportedConstraintType
struct UnsupportedConstraint{F<:AbstractFunction,S<:AbstractSet} <: UnsupportedError
     message::String
 end

An error indicating that constraints of type F-in-S are not supported by the model, that is, that supports_constraint returns false.

julia> import MathOptInterface as MOI
 
@@ -29,28 +29,28 @@
 form that is supported.
 
 To fix this error you must choose a different solver.
-
source
MathOptInterface.AddConstraintNotAllowedType
struct AddConstraintNotAllowed{F<:AbstractFunction, S<:AbstractSet} <: NotAllowedError
     message::String # Human-friendly explanation why the attribute cannot be set
-end

An error indicating that constraints of type F-in-S are supported (see supports_constraint) but cannot be added.

source
MathOptInterface.ModifyConstraintNotAllowedType
struct ModifyConstraintNotAllowed{F<:AbstractFunction, S<:AbstractSet,
                                   C<:AbstractFunctionModification} <: NotAllowedError
     constraint_index::ConstraintIndex{F, S}
     change::C
     message::String
-end

An error indicating that the constraint modification change cannot be applied to the constraint of index ci.

source
MathOptInterface.ModifyObjectiveNotAllowedType
struct ModifyObjectiveNotAllowed{C<:AbstractFunctionModification} <: NotAllowedError
+end

An error indicating that the constraint modification change cannot be applied to the constraint of index ci.

source
MathOptInterface.ModifyObjectiveNotAllowedType
struct ModifyObjectiveNotAllowed{C<:AbstractFunctionModification} <: NotAllowedError
     change::C
     message::String
-end

An error indicating that the objective modification change cannot be applied to the objective.

source
MathOptInterface.DeleteNotAllowedType
struct DeleteNotAllowed{IndexType <: Index} <: NotAllowedError
+end

An error indicating that the objective modification change cannot be applied to the objective.

source
MathOptInterface.DeleteNotAllowedType
struct DeleteNotAllowed{IndexType <: Index} <: NotAllowedError
     index::IndexType
     message::String
-end

An error indicating that the index index cannot be deleted.

source
MathOptInterface.UnsupportedSubmittableType
struct UnsupportedSubmittable{SubmitType} <: UnsupportedError
     sub::SubmitType
     message::String
-end

An error indicating that the submittable sub is not supported by the model, that is, that supports returns false.

source
MathOptInterface.SubmitNotAllowedType
struct SubmitNotAllowed{SubmitTyp<:AbstractSubmittable} <: NotAllowedError
+end

An error indicating that the submittable sub is not supported by the model, that is, that supports returns false.

source
MathOptInterface.SubmitNotAllowedType
struct SubmitNotAllowed{SubmitTyp<:AbstractSubmittable} <: NotAllowedError
     sub::SubmitType
     message::String
-end

An error indicating that the submittable sub is supported (see supports) but cannot be added for some reason (see the error string).

source
MathOptInterface.UnsupportedNonlinearOperatorType
UnsupportedNonlinearOperator(head::Symbol[, message::String]) <: UnsupportedError

An error thrown by optimizers if they do not support the operator head in a ScalarNonlinearFunction.

Example

julia> import MathOptInterface as MOI
+end

An error indicating that the submittable sub is supported (see supports) but cannot be added for some reason (see the error string).

source
MathOptInterface.UnsupportedNonlinearOperatorType
UnsupportedNonlinearOperator(head::Symbol[, message::String]) <: UnsupportedError

An error thrown by optimizers if they do not support the operator head in a ScalarNonlinearFunction.

Example

julia> import MathOptInterface as MOI
 
 julia> throw(MOI.UnsupportedNonlinearOperator(:black_box))
 ERROR: MathOptInterface.UnsupportedNonlinearOperator: The nonlinear operator `:black_box` is not supported by the model.
 Stacktrace:
-[...]
source

Note that setting the ConstraintFunction of a VariableIndex constraint is not allowed:

+[...]source

Note that setting the ConstraintFunction of a VariableIndex constraint is not allowed:

diff --git a/dev/reference/models/index.html b/dev/reference/models/index.html index 560c35f8cf..8db34b37f2 100644 --- a/dev/reference/models/index.html +++ b/dev/reference/models/index.html @@ -1,9 +1,9 @@ -Models · MathOptInterface

Models

Attribute interface

MathOptInterface.is_set_by_optimizeFunction
is_set_by_optimize(::AnyAttribute)

Return a Bool indicating whether the value of the attribute is set during an optimize! call, that is, the attribute is used to query the result of the optimization.

If an attribute can be set by the user, define is_copyable instead.

An attribute cannot be both is_copyable and is_set_by_optimize.

Default fallback

This function returns false by default so it should be implemented for attributes that are set by optimize!.

Undefined behavior

Querying the value of the attribute that is_set_by_optimize before a call to optimize! is undefined and depends on solver-specific behavior.

source
MathOptInterface.is_copyableFunction
is_copyable(::AnyAttribute)

Return a Bool indicating whether the value of the attribute may be copied during copy_to using set.

If an attribute is_copyable, then it cannot be modified by the optimizer, and get must always return the value that was set by the user.

If an attribute is the result of an optimization, define is_set_by_optimize instead.

An attribute cannot be both is_set_by_optimize and is_copyable.

Default fallback

By default is_copyable(attr) returns !is_set_by_optimize(attr), which is most probably true.

If an attribute should not be copied, define is_copyable(::MyAttribute) = false.

source
MathOptInterface.getFunction
MOI.get(b::AbstractBridge, ::MOI.NumberOfVariables)::Int64

Return the number of variables created by the bridge b in the model.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MOI.get(b::AbstractBridge, ::MOI.ListOfVariableIndices)

Return the list of variables created by the bridge b.

See also MOI.ListOfVariableIndices.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints{F,S})::Int64 where {F,S}

Return the number of constraints of the type F-in-S created by the bridge b.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MOI.get(b::AbstractBridge, ::MOI.ListOfConstraintIndices{F,S}) where {F,S}

Return a Vector{ConstraintIndex{F,S}} with indices of all constraints of type F-in-S created by the bride b.

See also MOI.ListOfConstraintIndices.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
function MOI.get(
+Models · MathOptInterface

Models

Attribute interface

MathOptInterface.is_set_by_optimizeFunction
is_set_by_optimize(::AnyAttribute)

Return a Bool indicating whether the value of the attribute is set during an optimize! call, that is, the attribute is used to query the result of the optimization.

If an attribute can be set by the user, define is_copyable instead.

An attribute cannot be both is_copyable and is_set_by_optimize.

Default fallback

This function returns false by default so it should be implemented for attributes that are set by optimize!.

Undefined behavior

Querying the value of the attribute that is_set_by_optimize before a call to optimize! is undefined and depends on solver-specific behavior.

source
MathOptInterface.is_copyableFunction
is_copyable(::AnyAttribute)

Return a Bool indicating whether the value of the attribute may be copied during copy_to using set.

If an attribute is_copyable, then it cannot be modified by the optimizer, and get must always return the value that was set by the user.

If an attribute is the result of an optimization, define is_set_by_optimize instead.

An attribute cannot be both is_set_by_optimize and is_copyable.

Default fallback

By default is_copyable(attr) returns !is_set_by_optimize(attr), which is most probably true.

If an attribute should not be copied, define is_copyable(::MyAttribute) = false.

source
MathOptInterface.getFunction
MOI.get(b::AbstractBridge, ::MOI.NumberOfVariables)::Int64

Return the number of variables created by the bridge b in the model.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MOI.get(b::AbstractBridge, ::MOI.ListOfVariableIndices)

Return the list of variables created by the bridge b.

See also MOI.ListOfVariableIndices.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints{F,S})::Int64 where {F,S}

Return the number of constraints of the type F-in-S created by the bridge b.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MOI.get(b::AbstractBridge, ::MOI.ListOfConstraintIndices{F,S}) where {F,S}

Return a Vector{ConstraintIndex{F,S}} with indices of all constraints of type F-in-S created by the bride b.

See also MOI.ListOfConstraintIndices.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
function MOI.get(
     model::MOI.ModelLike,
     attr::MOI.AbstractConstraintAttribute,
     bridge::AbstractBridge,
-)

Return the value of the attribute attr of the model model for the constraint bridged by bridge.

source
get(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute)

Return an attribute attr of the optimizer optimizer.

get(model::ModelLike, attr::AbstractModelAttribute)

Return an attribute attr of the model model.

get(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex)

If the attribute attr is set for the variable v in the model model, return its value, return nothing otherwise. If the attribute attr is not supported by model then an error should be thrown instead of returning nothing.

get(model::ModelLike, attr::AbstractVariableAttribute, v::Vector{VariableIndex})

Return a vector of attributes corresponding to each variable in the collection v in the model model.

get(model::ModelLike, attr::AbstractConstraintAttribute, c::ConstraintIndex)

If the attribute attr is set for the constraint c in the model model, return its value, return nothing otherwise. If the attribute attr is not supported by model then an error should be thrown instead of returning nothing.

get(
+)

Return the value of the attribute attr of the model model for the constraint bridged by bridge.

source
get(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute)

Return an attribute attr of the optimizer optimizer.

get(model::ModelLike, attr::AbstractModelAttribute)

Return an attribute attr of the model model.

get(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex)

If the attribute attr is set for the variable v in the model model, return its value, return nothing otherwise. If the attribute attr is not supported by model then an error should be thrown instead of returning nothing.

get(model::ModelLike, attr::AbstractVariableAttribute, v::Vector{VariableIndex})

Return a vector of attributes corresponding to each variable in the collection v in the model model.

get(model::ModelLike, attr::AbstractConstraintAttribute, c::ConstraintIndex)

If the attribute attr is set for the constraint c in the model model, return its value, return nothing otherwise. If the attribute attr is not supported by model then an error should be thrown instead of returning nothing.

get(
     model::ModelLike,
     attr::AbstractConstraintAttribute,
     c::Vector{ConstraintIndex{F,S}},
@@ -11,12 +11,12 @@
     model::ModelLike,
     ::Type{ConstraintIndex{F,S}},
     name::String,
-) where {F,S}

If an F-in-S constraint with name name exists in the model model, return the corresponding index, otherwise return nothing. Errors if two constraints have the same name.

get(model::ModelLike, ::Type{ConstraintIndex}, name::String)

If any constraint with name name exists in the model model, return the corresponding index, otherwise return nothing. This version is available for convenience but may incur a performance penalty because it is not type stable. Errors if two constraints have the same name.

source
MathOptInterface.get!Function
get!(output, model::ModelLike, args...)

An in-place version of get.

The signature matches that of get except that the result is placed in the vector output.

source
MathOptInterface.setFunction
function MOI.set(
+) where {F,S}

If an F-in-S constraint with name name exists in the model model, return the corresponding index, otherwise return nothing. Errors if two constraints have the same name.

get(model::ModelLike, ::Type{ConstraintIndex}, name::String)

If any constraint with name name exists in the model model, return the corresponding index, otherwise return nothing. This version is available for convenience but may incur a performance penalty because it is not type stable. Errors if two constraints have the same name.

source
MathOptInterface.get!Function
get!(output, model::ModelLike, args...)

An in-place version of get.

The signature matches that of get except that the result is placed in the vector output.

source
MathOptInterface.setFunction
function MOI.set(
     model::MOI.ModelLike,
     attr::MOI.AbstractConstraintAttribute,
     bridge::AbstractBridge,
     value,
-)

Set the value of the attribute attr of the model model for the constraint bridged by bridge.

source
set(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute, value)

Assign value to the attribute attr of the optimizer optimizer.

set(model::ModelLike, attr::AbstractModelAttribute, value)

Assign value to the attribute attr of the model model.

set(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex, value)

Assign value to the attribute attr of variable v in model model.

set(
+)

Set the value of the attribute attr of the model model for the constraint bridged by bridge.

source
set(optimizer::AbstractOptimizer, attr::AbstractOptimizerAttribute, value)

Assign value to the attribute attr of the optimizer optimizer.

set(model::ModelLike, attr::AbstractModelAttribute, value)

Assign value to the attribute attr of the model model.

set(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex, value)

Assign value to the attribute attr of variable v in model model.

set(
     model::ModelLike,
     attr::AbstractVariableAttribute,
     v::Vector{VariableIndex},
@@ -41,11 +41,11 @@
     ::ConstraintFunction,
     c::ConstraintIndex{F,S},
     func::F,
-) where {F,S}

Replace the function in constraint c with func. F must match the original function type used to define the constraint.

Note

Setting the constraint function is not allowed if F is VariableIndex; a SettingVariableIndexNotAllowed error is thrown instead. This is because, it would require changing the index c since the index of VariableIndex constraints must be the same as the index of the variable.

source
MathOptInterface.supportsFunction
MOI.supports(
+) where {F,S}

Replace the function in constraint c with func. F must match the original function type used to define the constraint.

Note

Setting the constraint function is not allowed if F is VariableIndex; a SettingVariableIndexNotAllowed error is thrown instead. This is because, it would require changing the index c since the index of VariableIndex constraints must be the same as the index of the variable.

source
MathOptInterface.supportsFunction
MOI.supports(
     model::MOI.ModelLike,
     attr::MOI.AbstractConstraintAttribute,
     BT::Type{<:AbstractBridge},
-)

Return a Bool indicating whether BT supports setting attr to model.

source
supports(model::ModelLike, sub::AbstractSubmittable)::Bool

Return a Bool indicating whether model supports the submittable sub.

supports(model::ModelLike, attr::AbstractOptimizerAttribute)::Bool

Return a Bool indicating whether model supports the optimizer attribute attr. That is, it returns false if copy_to(model, src) shows a warning in case attr is in the ListOfOptimizerAttributesSet of src; see copy_to for more details on how unsupported optimizer attributes are handled in copy.

supports(model::ModelLike, attr::AbstractModelAttribute)::Bool

Return a Bool indicating whether model supports the model attribute attr. That is, it returns false if copy_to(model, src) cannot be performed in case attr is in the ListOfModelAttributesSet of src.

supports(
+)

Return a Bool indicating whether BT supports setting attr to model.

source
supports(model::ModelLike, sub::AbstractSubmittable)::Bool

Return a Bool indicating whether model supports the submittable sub.

supports(model::ModelLike, attr::AbstractOptimizerAttribute)::Bool

Return a Bool indicating whether model supports the optimizer attribute attr. That is, it returns false if copy_to(model, src) shows a warning in case attr is in the ListOfOptimizerAttributesSet of src; see copy_to for more details on how unsupported optimizer attributes are handled in copy.

supports(model::ModelLike, attr::AbstractModelAttribute)::Bool

Return a Bool indicating whether model supports the model attribute attr. That is, it returns false if copy_to(model, src) cannot be performed in case attr is in the ListOfModelAttributesSet of src.

supports(
     model::ModelLike,
     attr::AbstractVariableAttribute,
     ::Type{VariableIndex},
@@ -53,7 +53,7 @@
     model::ModelLike,
     attr::AbstractConstraintAttribute,
     ::Type{ConstraintIndex{F,S}},
-)::Bool where {F,S}

Return a Bool indicating whether model supports the constraint attribute attr applied to an F-in-S constraint. That is, it returns false if copy_to(model, src) cannot be performed in case attr is in the ListOfConstraintAttributesSet of src.

For all five methods, if the attribute is only not supported in specific circumstances, it should still return true.

Note that supports is only defined for attributes for which is_copyable returns true as other attributes do not appear in the list of attributes set obtained by ListOfXXXAttributesSet.

source
MathOptInterface.attribute_value_typeFunction
attribute_value_type(attr::AnyAttribute)

Given an attribute attr, return the type of value expected by get, or returned by set.

Notes

  • Only implement this if it make sense to do so. If un-implemented, the default is Any.
source

Model interface

MathOptInterface.is_emptyFunction
is_empty(model::ModelLike)

Returns false if the model has any model attribute set or has any variables or constraints.

Note that an empty model can have optimizer attributes set.

source
MathOptInterface.empty!Function
empty!(model::ModelLike)

Empty the model, that is, remove all variables, constraints and model attributes but not optimizer attributes.

source
MathOptInterface.write_to_fileFunction
write_to_file(model::ModelLike, filename::String)

Write the current model to the file at filename.

Supported file types depend on the model type.

source
MathOptInterface.read_from_fileFunction
read_from_file(model::ModelLike, filename::String)

Read the file filename into the model model. If model is non-empty, this may throw an error.

Supported file types depend on the model type.

Note

Once the contents of the file are loaded into the model, users can query the variables via get(model, ListOfVariableIndices()). However, some filetypes, such as LP files, do not maintain an explicit ordering of the variables. Therefore, the returned list may be in an arbitrary order.

To avoid depending on the order of the indices, look up each variable index by name using get(model, VariableIndex, "name").

source
MathOptInterface.copy_toFunction
copy_to(dest::ModelLike, src::ModelLike)::IndexMap

Copy the model from src into dest.

The target dest is emptied, and all previous indices to variables and constraints in dest are invalidated.

Returns an IndexMap object that translates variable and constraint indices from the src model to the corresponding indices in the dest model.

Notes

AbstractOptimizerAttributes are not copied to the dest model.

IndexMap

Implementations of copy_to must return an IndexMap. For technical reasons, this type is defined in the Utilities submodule as MOI.Utilities.IndexMap. However, since it is an integral part of the MOI API, we provide MOI.IndexMap as an alias.

Example

# Given empty `ModelLike` objects `src` and `dest`.
+)::Bool where {F,S}

Return a Bool indicating whether model supports the constraint attribute attr applied to an F-in-S constraint. That is, it returns false if copy_to(model, src) cannot be performed in case attr is in the ListOfConstraintAttributesSet of src.

For all five methods, if the attribute is only not supported in specific circumstances, it should still return true.

Note that supports is only defined for attributes for which is_copyable returns true as other attributes do not appear in the list of attributes set obtained by ListOfXXXAttributesSet.

source
MathOptInterface.attribute_value_typeFunction
attribute_value_type(attr::AnyAttribute)

Given an attribute attr, return the type of value expected by get, or returned by set.

Notes

  • Only implement this if it make sense to do so. If un-implemented, the default is Any.
source

Model interface

MathOptInterface.is_emptyFunction
is_empty(model::ModelLike)

Returns false if the model has any model attribute set or has any variables or constraints.

Note that an empty model can have optimizer attributes set.

source
MathOptInterface.empty!Function
empty!(model::ModelLike)

Empty the model, that is, remove all variables, constraints and model attributes but not optimizer attributes.

source
MathOptInterface.write_to_fileFunction
write_to_file(model::ModelLike, filename::String)

Write the current model to the file at filename.

Supported file types depend on the model type.

source
MathOptInterface.read_from_fileFunction
read_from_file(model::ModelLike, filename::String)

Read the file filename into the model model. If model is non-empty, this may throw an error.

Supported file types depend on the model type.

Note

Once the contents of the file are loaded into the model, users can query the variables via get(model, ListOfVariableIndices()). However, some filetypes, such as LP files, do not maintain an explicit ordering of the variables. Therefore, the returned list may be in an arbitrary order.

To avoid depending on the order of the indices, look up each variable index by name using get(model, VariableIndex, "name").

source
MathOptInterface.copy_toFunction
copy_to(dest::ModelLike, src::ModelLike)::IndexMap

Copy the model from src into dest.

The target dest is emptied, and all previous indices to variables and constraints in dest are invalidated.

Returns an IndexMap object that translates variable and constraint indices from the src model to the corresponding indices in the dest model.

Notes

AbstractOptimizerAttributes are not copied to the dest model.

IndexMap

Implementations of copy_to must return an IndexMap. For technical reasons, this type is defined in the Utilities submodule as MOI.Utilities.IndexMap. However, since it is an integral part of the MOI API, we provide MOI.IndexMap as an alias.

Example

# Given empty `ModelLike` objects `src` and `dest`.
 
 x = add_variable(src)
 
@@ -62,7 +62,7 @@
 
 index_map = copy_to(dest, src)
 is_valid(dest, x) # false (unless index_map[x] == x)
-is_valid(dest, index_map[x]) # true
source
MathOptInterface.IndexMapType
IndexMap()

The dictionary-like object returned by copy_to.

IndexMap

Implementations of copy_to must return an IndexMap. For technical reasons, the IndexMap type is defined in the Utilities submodule as MOI.Utilities.IndexMap. However, since it is an integral part of the MOI API, we provide this MOI.IndexMap as an alias.

source

Model attributes

MathOptInterface.NameType
Name()

A model attribute for the string identifying the model. It has a default value of "" if not set`.

source
MathOptInterface.ObjectiveFunctionType
ObjectiveFunction{F<:AbstractScalarFunction}()

A model attribute for the objective function which has a type F<:AbstractScalarFunction.

F should be guaranteed to be equivalent but not necessarily identical to the function type provided by the user.

Throws an InexactError if the objective function cannot be converted to F, for example, the objective function is quadratic and F is ScalarAffineFunction{Float64} or it has non-integer coefficient and F is ScalarAffineFunction{Int}.

source
MathOptInterface.IndexMapType
IndexMap()

The dictionary-like object returned by copy_to.

IndexMap

Implementations of copy_to must return an IndexMap. For technical reasons, the IndexMap type is defined in the Utilities submodule as MOI.Utilities.IndexMap. However, since it is an integral part of the MOI API, we provide this MOI.IndexMap as an alias.

source

Model attributes

MathOptInterface.NameType
Name()

A model attribute for the string identifying the model. It has a default value of "" if not set`.

source
MathOptInterface.ObjectiveFunctionType
ObjectiveFunction{F<:AbstractScalarFunction}()

A model attribute for the objective function which has a type F<:AbstractScalarFunction.

F should be guaranteed to be equivalent but not necessarily identical to the function type provided by the user.

Throws an InexactError if the objective function cannot be converted to F, for example, the objective function is quadratic and F is ScalarAffineFunction{Float64} or it has non-integer coefficient and F is ScalarAffineFunction{Int}.

source
MathOptInterface.ObjectiveFunctionTypeType
ObjectiveFunctionType()

A model attribute for the type F of the objective function set using the ObjectiveFunction{F} attribute.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -72,7 +72,7 @@
 julia> MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x)
 
 julia> MOI.get(model, MOI.ObjectiveFunctionType())
-MathOptInterface.VariableIndex
source
MathOptInterface.ObjectiveSenseType
ObjectiveSense()

A model attribute for the objective sense of the objective function, which must be an OptimizationSense: MIN_SENSE, MAX_SENSE, or FEASIBILITY_SENSE. The default is FEASIBILITY_SENSE.

Interaction with ObjectiveFunction

Setting the sense to FEASIBILITY_SENSE unsets the ObjectiveFunction attribute. That is, if you first set ObjectiveFunction and then set ObjectiveSense to be FEASIBILITY_SENSE, no objective function will be passed to the solver.

In addition, some reformulations of ObjectiveFunction via bridges rely on the value of ObjectiveSense. Therefore, you should set ObjectiveSense before setting ObjectiveFunction.

source
MathOptInterface.ListOfModelAttributesSetType
ListOfModelAttributesSet()

A model attribute for the Vector{AbstractModelAttribute} of all model attributes attr such that:

  1. is_copyable(attr) returns true, and
  2. the attribute was set to the model
source
MathOptInterface.ListOfVariableAttributesSetType
ListOfVariableAttributesSet()

A model attribute for the Vector{AbstractVariableAttribute} of all variable attributes attr such that 1) is_copyable(attr) returns true and 2) the attribute was set to variables.

source
MathOptInterface.ListOfVariablesWithAttributeSetType
ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute)

A model attribute for the Vector{VariableIndex} of all variables with the attribute attr set.

The returned list may not be minimal, so some elements may have their default value set.

Note

This is an optional attribute to implement. The default fallback is to get ListOfVariableIndices.

source
MathOptInterface.ListOfConstraintsWithAttributeSetType
ListOfConstraintsWithAttributeSet{F,S}(attr:AbstractConstraintAttribute)

A model attribute for the Vector{ConstraintIndex{F,S}} of all constraints with the attribute attr set.

The returned list may not be minimal, so some elements may have their default value set.

Note

This is an optional attribute to implement. The default fallback is to get ListOfConstraintIndices.

source
MathOptInterface.UserDefinedFunctionType
UserDefinedFunction(name::Symbol, arity::Int) <: AbstractModelAttribute

Set this attribute to register a user-defined function by the name of name with arity arguments.

Once registered, name will appear in ListOfSupportedNonlinearOperators.

You cannot register multiple UserDefinedFunctions with the same name but different arity.

Value type

The value to be set is a tuple containing one, two, or three functions to evaluate the function, the first-order derivative, and the second-order derivative respectively. Both derivatives are optional, but if you pass the second-order derivative you must also pass the first-order derivative.

For univariate functions with arity == 1, the functions in the tuple must have the form:

  • f(x::T)::T: returns the value of the function at x
  • ∇f(x::T)::T: returns the first-order derivative of f with respect to x
  • ∇²f(x::T)::T: returns the second-order derivative of f with respect to x.

For multivariate functions with arity > 1, the functions in the tuple must have the form:

  • f(x::T...)::T: returns the value of the function at x
  • ∇f(g::AbstractVector{T}, x::T...)::Nothing: fills the components of g, with g[i] being the first-order partial derivative of f with respect to x[i]
  • ∇²f(H::AbstractMatrix{T}, x::T...)::Nothing: fills the non-zero components of H, with H[i, j] being the second-order partial derivative of f with respect to x[i] and then x[j]. H is initialized to the zero matrix, so you do not need to set any zero elements.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.VariableIndex
source
MathOptInterface.ObjectiveSenseType
ObjectiveSense()

A model attribute for the objective sense of the objective function, which must be an OptimizationSense: MIN_SENSE, MAX_SENSE, or FEASIBILITY_SENSE. The default is FEASIBILITY_SENSE.

Interaction with ObjectiveFunction

Setting the sense to FEASIBILITY_SENSE unsets the ObjectiveFunction attribute. That is, if you first set ObjectiveFunction and then set ObjectiveSense to be FEASIBILITY_SENSE, no objective function will be passed to the solver.

In addition, some reformulations of ObjectiveFunction via bridges rely on the value of ObjectiveSense. Therefore, you should set ObjectiveSense before setting ObjectiveFunction.

source
MathOptInterface.ListOfModelAttributesSetType
ListOfModelAttributesSet()

A model attribute for the Vector{AbstractModelAttribute} of all model attributes attr such that:

  1. is_copyable(attr) returns true, and
  2. the attribute was set to the model
source
MathOptInterface.ListOfVariableAttributesSetType
ListOfVariableAttributesSet()

A model attribute for the Vector{AbstractVariableAttribute} of all variable attributes attr such that 1) is_copyable(attr) returns true and 2) the attribute was set to variables.

source
MathOptInterface.ListOfVariablesWithAttributeSetType
ListOfVariablesWithAttributeSet(attr::AbstractVariableAttribute)

A model attribute for the Vector{VariableIndex} of all variables with the attribute attr set.

The returned list may not be minimal, so some elements may have their default value set.

Note

This is an optional attribute to implement. The default fallback is to get ListOfVariableIndices.

source
MathOptInterface.ListOfConstraintsWithAttributeSetType
ListOfConstraintsWithAttributeSet{F,S}(attr:AbstractConstraintAttribute)

A model attribute for the Vector{ConstraintIndex{F,S}} of all constraints with the attribute attr set.

The returned list may not be minimal, so some elements may have their default value set.

Note

This is an optional attribute to implement. The default fallback is to get ListOfConstraintIndices.

source
MathOptInterface.UserDefinedFunctionType
UserDefinedFunction(name::Symbol, arity::Int) <: AbstractModelAttribute

Set this attribute to register a user-defined function by the name of name with arity arguments.

Once registered, name will appear in ListOfSupportedNonlinearOperators.

You cannot register multiple UserDefinedFunctions with the same name but different arity.

Value type

The value to be set is a tuple containing one, two, or three functions to evaluate the function, the first-order derivative, and the second-order derivative respectively. Both derivatives are optional, but if you pass the second-order derivative you must also pass the first-order derivative.

For univariate functions with arity == 1, the functions in the tuple must have the form:

  • f(x::T)::T: returns the value of the function at x
  • ∇f(x::T)::T: returns the first-order derivative of f with respect to x
  • ∇²f(x::T)::T: returns the second-order derivative of f with respect to x.

For multivariate functions with arity > 1, the functions in the tuple must have the form:

  • f(x::T...)::T: returns the value of the function at x
  • ∇f(g::AbstractVector{T}, x::T...)::Nothing: fills the components of g, with g[i] being the first-order partial derivative of f with respect to x[i]
  • ∇²f(H::AbstractMatrix{T}, x::T...)::Nothing: fills the non-zero components of H, with H[i, j] being the second-order partial derivative of f with respect to x[i] and then x[j]. H is initialized to the zero matrix, so you do not need to set any zero elements.

Example

julia> import MathOptInterface as MOI
 
 julia> f(x, y) = x^2 + y^2
 f (generic function with 1 method)
@@ -114,18 +114,18 @@
  f(v[1], v[2])
 
 Subject to:
-
source

Optimizer interface

MathOptInterface.AbstractOptimizerType
AbstractOptimizer <: ModelLike

Abstract supertype for objects representing an instance of an optimization problem tied to a particular solver. This is typically a solver's in-memory representation. In addition to ModelLike, AbstractOptimizer objects let you solve the model and query the solution.

source

Optimizer interface

MathOptInterface.AbstractOptimizerType
AbstractOptimizer <: ModelLike

Abstract supertype for objects representing an instance of an optimization problem tied to a particular solver. This is typically a solver's in-memory representation. In addition to ModelLike, AbstractOptimizer objects let you solve the model and query the solution.

source
MathOptInterface.optimize!Method
optimize!(dest::AbstractOptimizer, src::ModelLike)::Tuple{IndexMap,Bool}

A "one-shot" call that copies the problem from src into dest and then uses dest to optimize the problem.

Returns a tuple of an IndexMap and a Bool copied.

  • The IndexMap object translates variable and constraint indices from the src model to the corresponding indices in the dest optimizer. See copy_to for details.
  • If copied == true, src was copied to dest and then cached, allowing incremental modification if supported by the solver.
  • If copied == false, a cache of the model was not kept in dest. Therefore, only the solution information (attributes for which is_set_by_optimize is true) is available to query.
Note

The main purpose of optimize! method with two arguments is for use in Utilities.CachingOptimizer.

Relationship to the single-argument optimize!

The default fallback of optimize!(dest::AbstractOptimizer, src::ModelLike) is

function optimize!(dest::AbstractOptimizer, src::ModelLike)
+end

Object grouping an optimizer constructor and a list of optimizer attributes. Instances are created with instantiate.

source
MathOptInterface.optimize!Method
optimize!(dest::AbstractOptimizer, src::ModelLike)::Tuple{IndexMap,Bool}

A "one-shot" call that copies the problem from src into dest and then uses dest to optimize the problem.

Returns a tuple of an IndexMap and a Bool copied.

  • The IndexMap object translates variable and constraint indices from the src model to the corresponding indices in the dest optimizer. See copy_to for details.
  • If copied == true, src was copied to dest and then cached, allowing incremental modification if supported by the solver.
  • If copied == false, a cache of the model was not kept in dest. Therefore, only the solution information (attributes for which is_set_by_optimize is true) is available to query.
Note

The main purpose of optimize! method with two arguments is for use in Utilities.CachingOptimizer.

Relationship to the single-argument optimize!

The default fallback of optimize!(dest::AbstractOptimizer, src::ModelLike) is

function optimize!(dest::AbstractOptimizer, src::ModelLike)
     index_map = copy_to(dest, src)
     optimize!(dest)
     return index_map, true
-end

Therefore, subtypes of AbstractOptimizer should either implement this two-argument method, or implement both copy_to(::Optimizer, ::ModelLike) and optimize!(::Optimizer).

source
MathOptInterface.instantiateFunction
instantiate(
     optimizer_constructor,
     with_cache_type::Union{Nothing,Type} = nothing,
     with_bridge_type::Union{Nothing,Type} = nothing,
-)

Create an instance of an optimizer by either:

  • calling optimizer_constructor.optimizer_constructor() and setting the parameters in optimizer_constructor.params if optimizer_constructor is a OptimizerWithAttributes
  • calling optimizer_constructor() if optimizer_constructor is callable.

withcachetype

If with_cache_type is not nothing, then the optimizer is wrapped in a Utilities.CachingOptimizer to store a cache of the model. This is most useful if the optimizer you are constructing does not support the incremental interface (see supports_incremental_interface).

withbridgetype

If with_bridge_type is not nothing, the optimizer is wrapped in a Bridges.full_bridge_optimizer, enabling all the bridges defined in the MOI.Bridges submodule with coefficient type with_bridge_type.

In addition, if the optimizer created by optimizer_constructor does not support the incremental interface (see supports_incremental_interface), then, irrespective of with_cache_type, the optimizer is wrapped in a Utilities.CachingOptimizer to store a cache of the bridged model.

If with_cache_type and with_bridge_type are both not nothing, then they must be the same type.

source
MathOptInterface.default_cacheFunction
default_cache(optimizer::ModelLike, ::Type{T}) where {T}

Return a new instance of the default model type to be used as cache for optimizer in a Utilities.CachingOptimizer for holding constraints of coefficient type T. By default, this returns Utilities.UniversalFallback(Utilities.Model{T}()). If copying from a instance of a given model type is faster for optimizer then a new method returning an instance of this model type should be defined.

source

Optimizer attributes

MathOptInterface.SolverVersionType
SolverVersion()

An optimizer attribute for the string identifying the version of the solver.

Note

For solvers supporting semantic versioning, the SolverVersion should be a string of the form "vMAJOR.MINOR.PATCH", so that it can be converted to a Julia VersionNumber (for example, `VersionNumber("v1.2.3")).

We do not require Semantic Versioning because some solvers use alternate versioning systems. For example, CPLEX uses Calendar Versioning, so SolverVersion will return a string like "202001".

source
MathOptInterface.SilentType
Silent()

An optimizer attribute for silencing the output of an optimizer. When set to true, it takes precedence over any other attribute controlling verbosity and requires the solver to produce no output. The default value is false which has no effect. In this case the verbosity is controlled by other attributes.

Note

Every optimizer should have verbosity on by default. For instance, if a solver has a solver-specific log level attribute, the MOI implementation should set it to 1 by default. If the user sets Silent to true, then the log level should be set to 0, even if the user specifically sets a value of log level. If the value of Silent is false then the log level set to the solver is the value given by the user for this solver-specific parameter or 1 if none is given.

source
MathOptInterface.TimeLimitSecType
TimeLimitSec()

An optimizer attribute for setting a time limit (in seconds) for an optimization. When set to nothing, it deactivates the solver time limit. The default value is nothing.

source
MathOptInterface.ObjectiveLimitType
ObjectiveLimit()

An optimizer attribute for setting a limit on the objective value.

The provided limit must be a Union{Real,Nothing}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

The solver may stop when the ObjectiveValue is better (lower for minimization, higher for maximization) than the ObjectiveLimit. If stopped, the TerminationStatus should be OBJECTIVE_LIMIT.

source
MathOptInterface.SolutionLimitType
SolutionLimit()

An optimizer attribute for setting a limit on the number of available feasible solutions.

Default values

The provided limit must be a Union{Nothing,Int}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

Termination criteria

The solver may stop when the ResultCount is larger than or equal to the SolutionLimit. If stopped because of this attribute, the TerminationStatus must be SOLUTION_LIMIT.

Solution quality

The quality of the available solutions is solver-dependent. The set of resulting solutions is not guaranteed to contain an optimal solution.

source
MathOptInterface.NodeLimitType
NodeLimit()

An optimizer attribute for setting a limit on the number of branch-and-bound nodes explored by a mixed-integer program (MIP) solver.

Default values

The provided limit must be a Union{Nothing,Int}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

Termination criteria

The solver may stop when the NodeCount is larger than or equal to the NodeLimit. If stopped because of this attribute, the TerminationStatus must be NODE_LIMIT.

source
MathOptInterface.NumberOfThreadsType
NumberOfThreads()

An optimizer attribute for setting the number of threads used for an optimization. When set to nothing uses solver default. Values are positive integers. The default value is nothing.

source
MathOptInterface.AbsoluteGapToleranceType
AbsoluteGapTolerance()

An optimizer attribute for setting the absolute gap tolerance for an optimization. This is an optimizer attribute, and should be set before calling optimize!. When set to nothing (if supported), uses solver default.

To set a relative gap tolerance, see RelativeGapTolerance.

Warning

The mathematical definition of "absolute gap", and its treatment during the optimization, are solver-dependent. However, assuming no other limit nor issue is encountered during the optimization, most solvers that implement this attribute will stop once $|f - b| ≤ g_{abs}$, where $b$ is the best bound, $f$ is the best feasible objective value, and $g_{abs}$ is the absolute gap.

source
MathOptInterface.RelativeGapToleranceType
RelativeGapTolerance()

An optimizer attribute for setting the relative gap tolerance for an optimization. This is an optimizer attribute, and should be set before calling optimize!. When set to nothing (if supported), uses solver default.

If you are looking for the relative gap of the current best solution, see RelativeGap. If no limit nor issue is encountered during the optimization, the value of RelativeGap should be at most as large as RelativeGapTolerance.

# Before optimizing: set relative gap tolerance
+)

Create an instance of an optimizer by either:

  • calling optimizer_constructor.optimizer_constructor() and setting the parameters in optimizer_constructor.params if optimizer_constructor is a OptimizerWithAttributes
  • calling optimizer_constructor() if optimizer_constructor is callable.

withcachetype

If with_cache_type is not nothing, then the optimizer is wrapped in a Utilities.CachingOptimizer to store a cache of the model. This is most useful if the optimizer you are constructing does not support the incremental interface (see supports_incremental_interface).

withbridgetype

If with_bridge_type is not nothing, the optimizer is wrapped in a Bridges.full_bridge_optimizer, enabling all the bridges defined in the MOI.Bridges submodule with coefficient type with_bridge_type.

In addition, if the optimizer created by optimizer_constructor does not support the incremental interface (see supports_incremental_interface), then, irrespective of with_cache_type, the optimizer is wrapped in a Utilities.CachingOptimizer to store a cache of the bridged model.

If with_cache_type and with_bridge_type are both not nothing, then they must be the same type.

source
MathOptInterface.default_cacheFunction
default_cache(optimizer::ModelLike, ::Type{T}) where {T}

Return a new instance of the default model type to be used as cache for optimizer in a Utilities.CachingOptimizer for holding constraints of coefficient type T. By default, this returns Utilities.UniversalFallback(Utilities.Model{T}()). If copying from a instance of a given model type is faster for optimizer then a new method returning an instance of this model type should be defined.

source

Optimizer attributes

MathOptInterface.SolverVersionType
SolverVersion()

An optimizer attribute for the string identifying the version of the solver.

Note

For solvers supporting semantic versioning, the SolverVersion should be a string of the form "vMAJOR.MINOR.PATCH", so that it can be converted to a Julia VersionNumber (for example, `VersionNumber("v1.2.3")).

We do not require Semantic Versioning because some solvers use alternate versioning systems. For example, CPLEX uses Calendar Versioning, so SolverVersion will return a string like "202001".

source
MathOptInterface.SilentType
Silent()

An optimizer attribute for silencing the output of an optimizer. When set to true, it takes precedence over any other attribute controlling verbosity and requires the solver to produce no output. The default value is false which has no effect. In this case the verbosity is controlled by other attributes.

Note

Every optimizer should have verbosity on by default. For instance, if a solver has a solver-specific log level attribute, the MOI implementation should set it to 1 by default. If the user sets Silent to true, then the log level should be set to 0, even if the user specifically sets a value of log level. If the value of Silent is false then the log level set to the solver is the value given by the user for this solver-specific parameter or 1 if none is given.

source
MathOptInterface.TimeLimitSecType
TimeLimitSec()

An optimizer attribute for setting a time limit (in seconds) for an optimization. When set to nothing, it deactivates the solver time limit. The default value is nothing.

source
MathOptInterface.ObjectiveLimitType
ObjectiveLimit()

An optimizer attribute for setting a limit on the objective value.

The provided limit must be a Union{Real,Nothing}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

The solver may stop when the ObjectiveValue is better (lower for minimization, higher for maximization) than the ObjectiveLimit. If stopped, the TerminationStatus should be OBJECTIVE_LIMIT.

source
MathOptInterface.SolutionLimitType
SolutionLimit()

An optimizer attribute for setting a limit on the number of available feasible solutions.

Default values

The provided limit must be a Union{Nothing,Int}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

Termination criteria

The solver may stop when the ResultCount is larger than or equal to the SolutionLimit. If stopped because of this attribute, the TerminationStatus must be SOLUTION_LIMIT.

Solution quality

The quality of the available solutions is solver-dependent. The set of resulting solutions is not guaranteed to contain an optimal solution.

source
MathOptInterface.NodeLimitType
NodeLimit()

An optimizer attribute for setting a limit on the number of branch-and-bound nodes explored by a mixed-integer program (MIP) solver.

Default values

The provided limit must be a Union{Nothing,Int}.

When set to nothing, the limit reverts to the solver's default.

The default value is nothing.

Termination criteria

The solver may stop when the NodeCount is larger than or equal to the NodeLimit. If stopped because of this attribute, the TerminationStatus must be NODE_LIMIT.

source
MathOptInterface.NumberOfThreadsType
NumberOfThreads()

An optimizer attribute for setting the number of threads used for an optimization. When set to nothing uses solver default. Values are positive integers. The default value is nothing.

source
MathOptInterface.AbsoluteGapToleranceType
AbsoluteGapTolerance()

An optimizer attribute for setting the absolute gap tolerance for an optimization. This is an optimizer attribute, and should be set before calling optimize!. When set to nothing (if supported), uses solver default.

To set a relative gap tolerance, see RelativeGapTolerance.

Warning

The mathematical definition of "absolute gap", and its treatment during the optimization, are solver-dependent. However, assuming no other limit nor issue is encountered during the optimization, most solvers that implement this attribute will stop once $|f - b| ≤ g_{abs}$, where $b$ is the best bound, $f$ is the best feasible objective value, and $g_{abs}$ is the absolute gap.

source
MathOptInterface.RelativeGapToleranceType
RelativeGapTolerance()

An optimizer attribute for setting the relative gap tolerance for an optimization. This is an optimizer attribute, and should be set before calling optimize!. When set to nothing (if supported), uses solver default.

If you are looking for the relative gap of the current best solution, see RelativeGap. If no limit nor issue is encountered during the optimization, the value of RelativeGap should be at most as large as RelativeGapTolerance.

# Before optimizing: set relative gap tolerance
 # set 0.1% relative gap tolerance
 MOI.set(model, MOI.RelativeGapTolerance(), 1e-3)
 MOI.optimize!(model)
@@ -135,4 +135,4 @@
 MOI.get(model, MOI.RelativeGapTolerance())  # returns 1e-3
 # ... and the relative gap of the obtained solution is smaller or equal to the
 # tolerance
-MOI.get(model, MOI.RelativeGap())  # should return something ≤ 1e-3
Warning

The mathematical definition of "relative gap", and its allowed range, are solver-dependent. Typically, solvers expect a value between 0.0 and 1.0.

source

List of attributes useful for optimizers

MathOptInterface.TerminationStatusCodeType
TerminationStatusCode

An Enum of possible values for the TerminationStatus attribute. This attribute is meant to explain the reason why the optimizer stopped executing in the most recent call to optimize!.

Values

Possible values are:

  • OPTIMIZE_NOT_CALLED: The algorithm has not started.
  • OPTIMAL: The algorithm found a globally optimal solution.
  • INFEASIBLE: The algorithm concluded that no feasible solution exists.
  • DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem. If, additionally, a feasible (primal) solution is known to exist, this status typically implies that the problem is unbounded, with some technical exceptions.
  • LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, could not find directions for improvement, or otherwise completed its search without global guarantees.
  • LOCALLY_INFEASIBLE: The algorithm converged to an infeasible point or otherwise completed its search without finding a feasible solution, without guarantees that no feasible solution exists.
  • INFEASIBLE_OR_UNBOUNDED: The algorithm stopped because it decided that the problem is infeasible or unbounded; this occasionally happens during MIP presolve.
  • ALMOST_OPTIMAL: The algorithm found a globally optimal solution to relaxed tolerances.
  • ALMOST_INFEASIBLE: The algorithm concluded that no feasible solution exists within relaxed tolerances.
  • ALMOST_DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem within relaxed tolerances.
  • ALMOST_LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, or could not find directions for improvement within relaxed tolerances.
  • ITERATION_LIMIT: An iterative algorithm stopped after conducting the maximum number of iterations.
  • TIME_LIMIT: The algorithm stopped after a user-specified computation time.
  • NODE_LIMIT: A branch-and-bound algorithm stopped because it explored a maximum number of nodes in the branch-and-bound tree.
  • SOLUTION_LIMIT: The algorithm stopped because it found the required number of solutions. This is often used in MIPs to get the solver to return the first feasible solution it encounters.
  • MEMORY_LIMIT: The algorithm stopped because it ran out of memory.
  • OBJECTIVE_LIMIT: The algorithm stopped because it found a solution better than a minimum limit set by the user.
  • NORM_LIMIT: The algorithm stopped because the norm of an iterate became too large.
  • OTHER_LIMIT: The algorithm stopped due to a limit not covered by one of the _LIMIT_ statuses above.
  • SLOW_PROGRESS: The algorithm stopped because it was unable to continue making progress towards the solution.
  • NUMERICAL_ERROR: The algorithm stopped because it encountered unrecoverable numerical error.
  • INVALID_MODEL: The algorithm stopped because the model is invalid.
  • INVALID_OPTION: The algorithm stopped because it was provided an invalid option.
  • INTERRUPTED: The algorithm stopped because of an interrupt signal.
  • OTHER_ERROR: The algorithm stopped because of an error not covered by one of the statuses defined above.
source
MathOptInterface.DUAL_INFEASIBLEConstant
DUAL_INFEASIBLE::TerminationStatusCode

An instance of the TerminationStatusCode enum.

DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem. If, additionally, a feasible (primal) solution is known to exist, this status typically implies that the problem is unbounded, with some technical exceptions.

source
MathOptInterface.LOCALLY_SOLVEDConstant
LOCALLY_SOLVED::TerminationStatusCode

An instance of the TerminationStatusCode enum.

LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, could not find directions for improvement, or otherwise completed its search without global guarantees.

source
MathOptInterface.LOCALLY_INFEASIBLEConstant
LOCALLY_INFEASIBLE::TerminationStatusCode

An instance of the TerminationStatusCode enum.

LOCALLY_INFEASIBLE: The algorithm converged to an infeasible point or otherwise completed its search without finding a feasible solution, without guarantees that no feasible solution exists.

source
MathOptInterface.SOLUTION_LIMITConstant
SOLUTION_LIMIT::TerminationStatusCode

An instance of the TerminationStatusCode enum.

SOLUTION_LIMIT: The algorithm stopped because it found the required number of solutions. This is often used in MIPs to get the solver to return the first feasible solution it encounters.

source
MathOptInterface.DualStatusType
DualStatus(result_index::Int = 1)

A model attribute for the ResultStatusCode of the dual result result_index. If result_index is omitted, it defaults to 1.

See ResultCount for information on how the results are ordered.

If result_index is larger than the value of ResultCount then NO_SOLUTION is returned.

source
MathOptInterface.ResultCountType
ResultCount()

A model attribute for the number of results available.

Order of solutions

A number of attributes contain an index, result_index, which is used to refer to one of the available results. Thus, result_index must be an integer between 1 and the number of available results.

As a general rule, the first result (result_index=1) is the most important result (for example, an optimal solution or an infeasibility certificate). Other results will typically be alternate solutions that the solver found during the search for the first result.

If a (local) optimal solution is available, that is, TerminationStatus is OPTIMAL or LOCALLY_SOLVED, the first result must correspond to the (locally) optimal solution. Other results may be alternative optimal solutions, or they may be other suboptimal solutions; use ObjectiveValue to distinguish between them.

If a primal or dual infeasibility certificate is available, that is, TerminationStatus is INFEASIBLE or DUAL_INFEASIBLE and the corresponding PrimalStatus or DualStatus is INFEASIBILITY_CERTIFICATE, then the first result must be a certificate. Other results may be alternate certificates, or infeasible points.

source
MathOptInterface.ObjectiveValueType
ObjectiveValue(result_index::Int = 1)

A model attribute for the objective value of the primal solution result_index.

If the solver does not have a primal value for the objective because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ObjectiveValue attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.DualObjectiveValueType
DualObjectiveValue(result_index::Int = 1)

A model attribute for the value of the objective function of the dual problem for the result_indexth dual result.

If the solver does not have a dual value for the objective because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a primal solution is available), the result is undefined. Users should first check DualStatus before accessing the DualObjectiveValue attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.RelativeGapType
RelativeGap()

A model attribute for the final relative optimality gap.

Warning

The definition of this gap is solver-dependent. However, most solvers implementing this attribute define the relative gap as some variation of $\frac{|b-f|}{|f|}$, where $b$ is the best bound and $f$ is the best feasible objective value.

source
MathOptInterface.SimplexIterationsType
SimplexIterations()

A model attribute for the cumulative number of simplex iterations during the optimization process.

For a mixed-integer program (MIP), the return value is the total simplex iterations for all nodes.

source
MathOptInterface.NodeCountType
NodeCount()

A model attribute for the total number of branch-and-bound nodes explored while solving a mixed-integer program (MIP).

source

ResultStatusCode

MathOptInterface.ResultStatusCodeType
ResultStatusCode

An Enum of possible values for the PrimalStatus and DualStatus attributes.

The values indicate how to interpret the result vector.

Values

Possible values are:

  • NO_SOLUTION: the result vector is empty.
  • FEASIBLE_POINT: the result vector is a feasible point.
  • NEARLY_FEASIBLE_POINT: the result vector is feasible if some constraint tolerances are relaxed.
  • INFEASIBLE_POINT: the result vector is an infeasible point.
  • INFEASIBILITY_CERTIFICATE: the result vector is an infeasibility certificate. If the PrimalStatus is INFEASIBILITY_CERTIFICATE, then the primal result vector is a certificate of dual infeasibility. If the DualStatus is INFEASIBILITY_CERTIFICATE, then the dual result vector is a proof of primal infeasibility.
  • NEARLY_INFEASIBILITY_CERTIFICATE: the result satisfies a relaxed criterion for a certificate of infeasibility.
  • REDUCTION_CERTIFICATE: the result vector is an ill-posed certificate; see this article for details. If the PrimalStatus is REDUCTION_CERTIFICATE, then the primal result vector is a proof that the dual problem is ill-posed. If the DualStatus is REDUCTION_CERTIFICATE, then the dual result vector is a proof that the primal is ill-posed.
  • NEARLY_REDUCTION_CERTIFICATE: the result satisfies a relaxed criterion for an ill-posed certificate.
  • UNKNOWN_RESULT_STATUS: the result vector contains a solution with an unknown interpretation.
  • OTHER_RESULT_STATUS: the result vector contains a solution with an interpretation not covered by one of the statuses defined above
source
MathOptInterface.INFEASIBILITY_CERTIFICATEConstant
INFEASIBILITY_CERTIFICATE::ResultStatusCode

An instance of the ResultStatusCode enum.

INFEASIBILITY_CERTIFICATE: the result vector is an infeasibility certificate. If the PrimalStatus is INFEASIBILITY_CERTIFICATE, then the primal result vector is a certificate of dual infeasibility. If the DualStatus is INFEASIBILITY_CERTIFICATE, then the dual result vector is a proof of primal infeasibility.

source
MathOptInterface.REDUCTION_CERTIFICATEConstant
REDUCTION_CERTIFICATE::ResultStatusCode

An instance of the ResultStatusCode enum.

REDUCTION_CERTIFICATE: the result vector is an ill-posed certificate; see this article for details. If the PrimalStatus is REDUCTION_CERTIFICATE, then the primal result vector is a proof that the dual problem is ill-posed. If the DualStatus is REDUCTION_CERTIFICATE, then the dual result vector is a proof that the primal is ill-posed.

source

Conflict Status

MathOptInterface.compute_conflict!Function
compute_conflict!(optimizer::AbstractOptimizer)

Computes a minimal subset of constraints such that the model with the other constraint removed is still infeasible.

Some solvers call a set of conflicting constraints an Irreducible Inconsistent Subsystem (IIS).

See also ConflictStatus and ConstraintConflictStatus.

Note

If the model is modified after a call to compute_conflict!, the implementor is not obliged to purge the conflict. Any calls to the above attributes may return values for the original conflict without a warning. Similarly, when modifying the model, the conflict can be discarded.

source
MathOptInterface.ConflictStatusCodeType
ConflictStatusCode

An Enum of possible values for the ConflictStatus attribute. This attribute is meant to explain the reason why the conflict finder stopped executing in the most recent call to compute_conflict!.

Possible values are:

  • COMPUTE_CONFLICT_NOT_CALLED: the function compute_conflict! has not yet been called
  • NO_CONFLICT_EXISTS: there is no conflict because the problem is feasible
  • NO_CONFLICT_FOUND: the solver could not find a conflict
  • CONFLICT_FOUND: at least one conflict could be found
source
MathOptInterface.ConflictParticipationStatusCodeType
ConflictParticipationStatusCode

An Enum of possible values for the ConstraintConflictStatus attribute. This attribute is meant to indicate whether a given constraint participates or not in the last computed conflict.

Values

Possible values are:

  • NOT_IN_CONFLICT: the constraint does not participate in the conflict
  • IN_CONFLICT: the constraint participates in the conflict
  • MAYBE_IN_CONFLICT: the constraint may participate in the conflict, the solver was not able to prove that the constraint can be excluded from the conflict
source
+MOI.get(model, MOI.RelativeGap()) # should return something ≤ 1e-3
Warning

The mathematical definition of "relative gap", and its allowed range, are solver-dependent. Typically, solvers expect a value between 0.0 and 1.0.

source

List of attributes useful for optimizers

MathOptInterface.TerminationStatusCodeType
TerminationStatusCode

An Enum of possible values for the TerminationStatus attribute. This attribute is meant to explain the reason why the optimizer stopped executing in the most recent call to optimize!.

Values

Possible values are:

  • OPTIMIZE_NOT_CALLED: The algorithm has not started.
  • OPTIMAL: The algorithm found a globally optimal solution.
  • INFEASIBLE: The algorithm concluded that no feasible solution exists.
  • DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem. If, additionally, a feasible (primal) solution is known to exist, this status typically implies that the problem is unbounded, with some technical exceptions.
  • LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, could not find directions for improvement, or otherwise completed its search without global guarantees.
  • LOCALLY_INFEASIBLE: The algorithm converged to an infeasible point or otherwise completed its search without finding a feasible solution, without guarantees that no feasible solution exists.
  • INFEASIBLE_OR_UNBOUNDED: The algorithm stopped because it decided that the problem is infeasible or unbounded; this occasionally happens during MIP presolve.
  • ALMOST_OPTIMAL: The algorithm found a globally optimal solution to relaxed tolerances.
  • ALMOST_INFEASIBLE: The algorithm concluded that no feasible solution exists within relaxed tolerances.
  • ALMOST_DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem within relaxed tolerances.
  • ALMOST_LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, or could not find directions for improvement within relaxed tolerances.
  • ITERATION_LIMIT: An iterative algorithm stopped after conducting the maximum number of iterations.
  • TIME_LIMIT: The algorithm stopped after a user-specified computation time.
  • NODE_LIMIT: A branch-and-bound algorithm stopped because it explored a maximum number of nodes in the branch-and-bound tree.
  • SOLUTION_LIMIT: The algorithm stopped because it found the required number of solutions. This is often used in MIPs to get the solver to return the first feasible solution it encounters.
  • MEMORY_LIMIT: The algorithm stopped because it ran out of memory.
  • OBJECTIVE_LIMIT: The algorithm stopped because it found a solution better than a minimum limit set by the user.
  • NORM_LIMIT: The algorithm stopped because the norm of an iterate became too large.
  • OTHER_LIMIT: The algorithm stopped due to a limit not covered by one of the _LIMIT_ statuses above.
  • SLOW_PROGRESS: The algorithm stopped because it was unable to continue making progress towards the solution.
  • NUMERICAL_ERROR: The algorithm stopped because it encountered unrecoverable numerical error.
  • INVALID_MODEL: The algorithm stopped because the model is invalid.
  • INVALID_OPTION: The algorithm stopped because it was provided an invalid option.
  • INTERRUPTED: The algorithm stopped because of an interrupt signal.
  • OTHER_ERROR: The algorithm stopped because of an error not covered by one of the statuses defined above.
source
MathOptInterface.DUAL_INFEASIBLEConstant
DUAL_INFEASIBLE::TerminationStatusCode

An instance of the TerminationStatusCode enum.

DUAL_INFEASIBLE: The algorithm concluded that no dual bound exists for the problem. If, additionally, a feasible (primal) solution is known to exist, this status typically implies that the problem is unbounded, with some technical exceptions.

source
MathOptInterface.LOCALLY_SOLVEDConstant
LOCALLY_SOLVED::TerminationStatusCode

An instance of the TerminationStatusCode enum.

LOCALLY_SOLVED: The algorithm converged to a stationary point, local optimal solution, could not find directions for improvement, or otherwise completed its search without global guarantees.

source
MathOptInterface.LOCALLY_INFEASIBLEConstant
LOCALLY_INFEASIBLE::TerminationStatusCode

An instance of the TerminationStatusCode enum.

LOCALLY_INFEASIBLE: The algorithm converged to an infeasible point or otherwise completed its search without finding a feasible solution, without guarantees that no feasible solution exists.

source
MathOptInterface.SOLUTION_LIMITConstant
SOLUTION_LIMIT::TerminationStatusCode

An instance of the TerminationStatusCode enum.

SOLUTION_LIMIT: The algorithm stopped because it found the required number of solutions. This is often used in MIPs to get the solver to return the first feasible solution it encounters.

source
MathOptInterface.DualStatusType
DualStatus(result_index::Int = 1)

A model attribute for the ResultStatusCode of the dual result result_index. If result_index is omitted, it defaults to 1.

See ResultCount for information on how the results are ordered.

If result_index is larger than the value of ResultCount then NO_SOLUTION is returned.

source
MathOptInterface.ResultCountType
ResultCount()

A model attribute for the number of results available.

Order of solutions

A number of attributes contain an index, result_index, which is used to refer to one of the available results. Thus, result_index must be an integer between 1 and the number of available results.

As a general rule, the first result (result_index=1) is the most important result (for example, an optimal solution or an infeasibility certificate). Other results will typically be alternate solutions that the solver found during the search for the first result.

If a (local) optimal solution is available, that is, TerminationStatus is OPTIMAL or LOCALLY_SOLVED, the first result must correspond to the (locally) optimal solution. Other results may be alternative optimal solutions, or they may be other suboptimal solutions; use ObjectiveValue to distinguish between them.

If a primal or dual infeasibility certificate is available, that is, TerminationStatus is INFEASIBLE or DUAL_INFEASIBLE and the corresponding PrimalStatus or DualStatus is INFEASIBILITY_CERTIFICATE, then the first result must be a certificate. Other results may be alternate certificates, or infeasible points.

source
MathOptInterface.ObjectiveValueType
ObjectiveValue(result_index::Int = 1)

A model attribute for the objective value of the primal solution result_index.

If the solver does not have a primal value for the objective because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the ObjectiveValue attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.DualObjectiveValueType
DualObjectiveValue(result_index::Int = 1)

A model attribute for the value of the objective function of the dual problem for the result_indexth dual result.

If the solver does not have a dual value for the objective because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a primal solution is available), the result is undefined. Users should first check DualStatus before accessing the DualObjectiveValue attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.RelativeGapType
RelativeGap()

A model attribute for the final relative optimality gap.

Warning

The definition of this gap is solver-dependent. However, most solvers implementing this attribute define the relative gap as some variation of $\frac{|b-f|}{|f|}$, where $b$ is the best bound and $f$ is the best feasible objective value.

source
MathOptInterface.SimplexIterationsType
SimplexIterations()

A model attribute for the cumulative number of simplex iterations during the optimization process.

For a mixed-integer program (MIP), the return value is the total simplex iterations for all nodes.

source
MathOptInterface.NodeCountType
NodeCount()

A model attribute for the total number of branch-and-bound nodes explored while solving a mixed-integer program (MIP).

source

ResultStatusCode

MathOptInterface.ResultStatusCodeType
ResultStatusCode

An Enum of possible values for the PrimalStatus and DualStatus attributes.

The values indicate how to interpret the result vector.

Values

Possible values are:

  • NO_SOLUTION: the result vector is empty.
  • FEASIBLE_POINT: the result vector is a feasible point.
  • NEARLY_FEASIBLE_POINT: the result vector is feasible if some constraint tolerances are relaxed.
  • INFEASIBLE_POINT: the result vector is an infeasible point.
  • INFEASIBILITY_CERTIFICATE: the result vector is an infeasibility certificate. If the PrimalStatus is INFEASIBILITY_CERTIFICATE, then the primal result vector is a certificate of dual infeasibility. If the DualStatus is INFEASIBILITY_CERTIFICATE, then the dual result vector is a proof of primal infeasibility.
  • NEARLY_INFEASIBILITY_CERTIFICATE: the result satisfies a relaxed criterion for a certificate of infeasibility.
  • REDUCTION_CERTIFICATE: the result vector is an ill-posed certificate; see this article for details. If the PrimalStatus is REDUCTION_CERTIFICATE, then the primal result vector is a proof that the dual problem is ill-posed. If the DualStatus is REDUCTION_CERTIFICATE, then the dual result vector is a proof that the primal is ill-posed.
  • NEARLY_REDUCTION_CERTIFICATE: the result satisfies a relaxed criterion for an ill-posed certificate.
  • UNKNOWN_RESULT_STATUS: the result vector contains a solution with an unknown interpretation.
  • OTHER_RESULT_STATUS: the result vector contains a solution with an interpretation not covered by one of the statuses defined above
source
MathOptInterface.INFEASIBILITY_CERTIFICATEConstant
INFEASIBILITY_CERTIFICATE::ResultStatusCode

An instance of the ResultStatusCode enum.

INFEASIBILITY_CERTIFICATE: the result vector is an infeasibility certificate. If the PrimalStatus is INFEASIBILITY_CERTIFICATE, then the primal result vector is a certificate of dual infeasibility. If the DualStatus is INFEASIBILITY_CERTIFICATE, then the dual result vector is a proof of primal infeasibility.

source
MathOptInterface.REDUCTION_CERTIFICATEConstant
REDUCTION_CERTIFICATE::ResultStatusCode

An instance of the ResultStatusCode enum.

REDUCTION_CERTIFICATE: the result vector is an ill-posed certificate; see this article for details. If the PrimalStatus is REDUCTION_CERTIFICATE, then the primal result vector is a proof that the dual problem is ill-posed. If the DualStatus is REDUCTION_CERTIFICATE, then the dual result vector is a proof that the primal is ill-posed.

source

Conflict Status

MathOptInterface.compute_conflict!Function
compute_conflict!(optimizer::AbstractOptimizer)

Computes a minimal subset of constraints such that the model with the other constraint removed is still infeasible.

Some solvers call a set of conflicting constraints an Irreducible Inconsistent Subsystem (IIS).

See also ConflictStatus and ConstraintConflictStatus.

Note

If the model is modified after a call to compute_conflict!, the implementor is not obliged to purge the conflict. Any calls to the above attributes may return values for the original conflict without a warning. Similarly, when modifying the model, the conflict can be discarded.

source
MathOptInterface.ConflictStatusCodeType
ConflictStatusCode

An Enum of possible values for the ConflictStatus attribute. This attribute is meant to explain the reason why the conflict finder stopped executing in the most recent call to compute_conflict!.

Possible values are:

  • COMPUTE_CONFLICT_NOT_CALLED: the function compute_conflict! has not yet been called
  • NO_CONFLICT_EXISTS: there is no conflict because the problem is feasible
  • NO_CONFLICT_FOUND: the solver could not find a conflict
  • CONFLICT_FOUND: at least one conflict could be found
source
MathOptInterface.ConflictParticipationStatusCodeType
ConflictParticipationStatusCode

An Enum of possible values for the ConstraintConflictStatus attribute. This attribute is meant to indicate whether a given constraint participates or not in the last computed conflict.

Values

Possible values are:

  • NOT_IN_CONFLICT: the constraint does not participate in the conflict
  • IN_CONFLICT: the constraint participates in the conflict
  • MAYBE_IN_CONFLICT: the constraint may participate in the conflict, the solver was not able to prove that the constraint can be excluded from the conflict
source
diff --git a/dev/reference/modification/index.html b/dev/reference/modification/index.html index dcb00cc206..00b97d3e5e 100644 --- a/dev/reference/modification/index.html +++ b/dev/reference/modification/index.html @@ -19,7 +19,7 @@ Subject to: ScalarAffineFunction{Float64}-in-EqualTo{Float64} - 10.0 + 1.0 v[1] == 1.0source
modify(
+ 10.0 + 1.0 v[1] == 1.0
source
modify(
     model::ModelLike,
     cis::AbstractVector{<:ConstraintIndex},
     changes::AbstractVector{<:AbstractFunctionModification},
@@ -40,7 +40,7 @@
 
 ScalarAffineFunction{Float64}-in-EqualTo{Float64}
  0.0 + 2.0 v[1] == 1.0
- 0.0 + 0.5 v[2] == 1.0
source
modify(model::ModelLike, ::ObjectiveFunction, change::AbstractFunctionModification)

Apply the modification specified by change to the objective function of model. To change the function completely, call set instead.

An ModifyObjectiveNotAllowed error is thrown if modifying objectives is not supported by the model model.

Example

julia> import MathOptInterface as MOI
+ 0.0 + 0.5 v[2] == 1.0
source
modify(model::ModelLike, ::ObjectiveFunction, change::AbstractFunctionModification)

Apply the modification specified by change to the objective function of model. To change the function completely, call set instead.

An ModifyObjectiveNotAllowed error is thrown if modifying objectives is not supported by the model model.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -61,7 +61,7 @@
 Minimize ScalarAffineFunction{Float64}:
  10.0 + 1.0 v[1]
 
-Subject to:
source
modify(
+Subject to:
source
modify(
     model::ModelLike,
     attr::ObjectiveFunction,
     changes::AbstractVector{<:AbstractFunctionModification},
@@ -86,11 +86,11 @@
 Minimize ScalarAffineFunction{Float64}:
  0.0 + 2.0 v[1] + 0.5 v[2]
 
-Subject to:
source
MathOptInterface.AbstractFunctionModificationType
AbstractFunctionModification

An abstract supertype for structs which specify partial modifications to functions, to be used for making small modifications instead of replacing the functions entirely.

source
MathOptInterface.AbstractFunctionModificationType
AbstractFunctionModification

An abstract supertype for structs which specify partial modifications to functions, to be used for making small modifications instead of replacing the functions entirely.

source
MathOptInterface.ScalarQuadraticCoefficientChangeType
ScalarQuadraticCoefficientChange{T}(
     variable_1::VariableIndex,
     variable_2::VariableIndex,
     new_coefficient::T,
-)

A struct used to request a change in the quadratic coefficient of a ScalarQuadraticFunction.

Scaling factors

A ScalarQuadraticFunction has an implicit 0.5 scaling factor in front of the Q matrix. This modification applies to terms in the Q matrix.

If variable_1 == variable_2, this modification sets the corresponding diagonal element of the Q matrix to new_coefficient.

If variable_1 != variable_2, this modification is equivalent to setting both the corresponding upper- and lower-triangular elements of the Q matrix to new_coefficient.

As a consequence:

  • to modify the term x^2 to become 2x^2, new_coefficient must be 4
  • to modify the term xy to become 2xy, new_coefficient must be 2
source
MathOptInterface.MultirowChangeType
MultirowChange{T}(
+)

A struct used to request a change in the quadratic coefficient of a ScalarQuadraticFunction.

Scaling factors

A ScalarQuadraticFunction has an implicit 0.5 scaling factor in front of the Q matrix. This modification applies to terms in the Q matrix.

If variable_1 == variable_2, this modification sets the corresponding diagonal element of the Q matrix to new_coefficient.

If variable_1 != variable_2, this modification is equivalent to setting both the corresponding upper- and lower-triangular elements of the Q matrix to new_coefficient.

As a consequence:

  • to modify the term x^2 to become 2x^2, new_coefficient must be 4
  • to modify the term xy to become 2xy, new_coefficient must be 2
source
+) where {T}

A struct used to request a change in the linear coefficients of a single variable in a vector-valued function.

New coefficients are specified by (output_index, coefficient) tuples.

Applicable to VectorAffineFunction and VectorQuadraticFunction.

source diff --git a/dev/reference/nonlinear/index.html b/dev/reference/nonlinear/index.html index a9a495e043..9e9cd419a3 100644 --- a/dev/reference/nonlinear/index.html +++ b/dev/reference/nonlinear/index.html @@ -4,12 +4,12 @@ julia> evaluator = MOI.Test.HS071(true); julia> supertype(typeof(evaluator)) -MathOptInterface.AbstractNLPEvaluatorsource
MathOptInterface.NLPBoundsPairType
NLPBoundsPair(lower::Float64, upper::Float64)

A struct holding a pair of lower and upper bounds.

-Inf and Inf can be used to indicate no lower or upper bound, respectively.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.AbstractNLPEvaluator
source
MathOptInterface.NLPBoundsPairType
NLPBoundsPair(lower::Float64, upper::Float64)

A struct holding a pair of lower and upper bounds.

-Inf and Inf can be used to indicate no lower or upper bound, respectively.

Example

julia> import MathOptInterface as MOI
 
 julia> bounds = MOI.NLPBoundsPair.([25.0, 40.0], [Inf, 40.0])
 2-element Vector{MathOptInterface.NLPBoundsPair}:
  MathOptInterface.NLPBoundsPair(25.0, Inf)
- MathOptInterface.NLPBoundsPair(40.0, 40.0)
source
MathOptInterface.NLPBlockDataType
struct NLPBlockData
     constraint_bounds::Vector{NLPBoundsPair}
     evaluator::AbstractNLPEvaluator
     has_objective::Bool
@@ -23,7 +23,7 @@
            true,
        );
 
-julia> MOI.set(model, MOI.NLPBlock(), block)
source

Attributes

Attributes

MathOptInterface.NLPBlockType
NLPBlock()

An AbstractModelAttribute that stores an NLPBlockData, representing a set of nonlinear constraints, and optionally a nonlinear objective.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}());
 
@@ -33,13 +33,13 @@
            true,
        );
 
-julia> MOI.set(model, MOI.NLPBlock(), block)
source
MathOptInterface.NLPBlockDualType
NLPBlockDual(result_index::Int = 1)

An AbstractModelAttribute for the Lagrange multipliers on the constraints from the NLPBlock in result result_index.

If result_index is omitted, it is 1 by default.

Example

julia> import MathOptInterface as MOI
+julia> MOI.set(model, MOI.NLPBlock(), block)
source
MathOptInterface.NLPBlockDualType
NLPBlockDual(result_index::Int = 1)

An AbstractModelAttribute for the Lagrange multipliers on the constraints from the NLPBlock in result result_index.

If result_index is omitted, it is 1 by default.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.NLPBlockDual()
 MathOptInterface.NLPBlockDual(1)
 
 julia> MOI.NLPBlockDual(2)
-MathOptInterface.NLPBlockDual(2)
source
MathOptInterface.NLPBlockDualStartType
NLPBlockDualStart()

An AbstractModelAttribute for the initial assignment of the Lagrange multipliers on the constraints from the NLPBlock that the solver may use to warm-start the solve.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}());
 
@@ -51,14 +51,14 @@
 
 julia> MOI.set(model, MOI.NLPBlock(), block)
 
-julia> MOI.set(model, MOI.NLPBlockDualStart(), [1.0, 2.0])
source

Functions

Functions

MathOptInterface.initializeFunction
initialize(
     d::AbstractNLPEvaluator,
     requested_features::Vector{Symbol},
 )::Nothing

Initialize d with the set of features in requested_features. Check features_available before calling initialize to see what features are supported by d.

Warning

This method must be called before any other methods.

Features

The following features are defined:

In all cases, including when requested_features is empty, eval_objective and eval_constraint are supported.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
-julia> MOI.initialize(evaluator, [:Grad, :Jac])
source
MathOptInterface.features_availableFunction
features_available(d::AbstractNLPEvaluator)::Vector{Symbol}

Returns the subset of features available for this problem instance.

See initialize for the list of defined features.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+julia> MOI.initialize(evaluator, [:Grad, :Jac])
source
MathOptInterface.features_availableFunction
features_available(d::AbstractNLPEvaluator)::Vector{Symbol}

Returns the subset of features available for this problem instance.

See initialize for the list of defined features.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true, true);
 
@@ -69,14 +69,14 @@
  :JacVec
  :ExprGraph
  :Hess
- :HessVec
source
MathOptInterface.eval_objectiveFunction
eval_objective(d::AbstractNLPEvaluator, x::AbstractVector{T})::T where {T}

Evaluate the objective $f(x)$, returning a scalar value.

Initialize

Before calling this function, you must call initialize, but you do not need to pass a value.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+ :HessVec
source
MathOptInterface.eval_objectiveFunction
eval_objective(d::AbstractNLPEvaluator, x::AbstractVector{T})::T where {T}

Evaluate the objective $f(x)$, returning a scalar value.

Initialize

Before calling this function, you must call initialize, but you do not need to pass a value.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
 julia> MOI.initialize(evaluator, Symbol[])
 
 julia> MOI.eval_objective(evaluator, [1.0, 2.0, 3.0, 4.0])
-27.0
source
MathOptInterface.eval_constraintFunction
eval_constraint(
     d::AbstractNLPEvaluator,
     g::AbstractVector{T},
     x::AbstractVector{T},
@@ -93,7 +93,7 @@
 julia> g
 2-element Vector{Float64}:
  24.0
- 30.0
source
MathOptInterface.jacobian_structureFunction
jacobian_structure(d::AbstractNLPEvaluator)::Vector{Tuple{Int64,Int64}}

Returns a vector of tuples, (row, column), where each indicates the position of a structurally nonzero element in the Jacobian matrix: $J_g(x) = \left[ \begin{array}{c} \nabla g_1(x) \\ \nabla g_2(x) \\ \vdots \\ \nabla g_m(x) \end{array}\right],$ where $g_i$ is the $i\text{th}$ component of the nonlinear constraints $g(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Jac.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+  6.0
source
MathOptInterface.jacobian_structureFunction
jacobian_structure(d::AbstractNLPEvaluator)::Vector{Tuple{Int64,Int64}}

Returns a vector of tuples, (row, column), where each indicates the position of a structurally nonzero element in the Jacobian matrix: $J_g(x) = \left[ \begin{array}{c} \nabla g_1(x) \\ \nabla g_2(x) \\ \vdots \\ \nabla g_m(x) \end{array}\right],$ where $g_i$ is the $i\text{th}$ component of the nonlinear constraints $g(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Jac.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
@@ -127,7 +127,7 @@
  (2, 1)
  (2, 2)
  (2, 3)
- (2, 4)
source
MathOptInterface.constraint_gradient_structureFunction
constraint_gradient_structure(d::AbstractNLPEvaluator, i::Int)::Vector{Int64}

Returns a vector of indices, where each element indicates the position of a structurally nonzero element in the gradient of constraint $\nabla g_i(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Jac.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+  6.0
source
MathOptInterface.constraint_gradient_structureFunction
constraint_gradient_structure(d::AbstractNLPEvaluator, i::Int)::Vector{Int64}

Returns a vector of indices, where each element indicates the position of a structurally nonzero element in the gradient of constraint $\nabla g_i(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Jac.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
@@ -160,7 +160,7 @@
  1
  2
  3
- 4
source
MathOptInterface.hessian_lagrangian_structureFunction
hessian_lagrangian_structure(
     d::AbstractNLPEvaluator,
 )::Vector{Tuple{Int64,Int64}}

Returns a vector of tuples, (row, column), where each indicates the position of a structurally nonzero element in the Hessian-of-the-Lagrangian matrix: $\nabla^2 f(x) + \sum_{i=1}^m \nabla^2 g_i(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

Any mix of lower and upper-triangular indices is valid. Elements (i, j) and (j, i), if both present, should be treated as duplicates.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Hess.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
@@ -250,7 +250,7 @@
  (4, 1)
  (4, 2)
  (4, 3)
- (4, 4)
source
MathOptInterface.hessian_objective_structureFunction
hessian_objective_structure(
     d::AbstractNLPEvaluator,
 )::Vector{Tuple{Int64,Int64}}

Returns a vector of tuples, (row, column), where each indicates the position of a structurally nonzero element in the Hessian matrix: $\nabla^2 f(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

Any mix of lower and upper-triangular indices is valid. Elements (i, j) and (j, i), if both present, should be treated as duplicates.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Hess.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
@@ -265,7 +265,7 @@
  (3, 1)
  (4, 1)
  (4, 2)
- (4, 3)
source
MathOptInterface.hessian_constraint_structureFunction
hessian_constraint_structure(
     d::AbstractNLPEvaluator,
     i::Int64,
 )::Vector{Tuple{Int64,Int64}}

Returns a vector of tuples, (row, column), where each indicates the position of a structurally nonzero element in the Hessian matrix: $\nabla^2 g_i(x)$.

The indices are not required to be sorted and can contain duplicates, in which case the solver should combine the corresponding elements by adding them together.

Any mix of lower and upper-triangular indices is valid. Elements (i, j) and (j, i), if both present, should be treated as duplicates.

The sparsity structure is assumed to be independent of the point $x$.

Initialize

Before calling this function, you must call initialize with :Hess.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
@@ -288,7 +288,7 @@
  (1, 1)
  (2, 2)
  (3, 3)
- (4, 4)
source
MathOptInterface.objective_exprFunction
objective_expr(d::AbstractNLPEvaluator)::Expr

Returns a Julia Expr object representing the expression graph of the objective function.

Format

The expression has a number of limitations, compared with arbitrary Julia expressions:

  • All sums and products are flattened out as simple Expr(:+, ...) and Expr(:*, ...) objects.
  • All decision variables must be of the form Expr(:ref, :x, MOI.VariableIndex(i)), where i is the $i$th variable in ListOfVariableIndices.
  • There are currently no restrictions on recognized functions; typically these will be built-in Julia functions like ^, exp, log, cos, tan, sqrt, etc., but modeling interfaces may choose to extend these basic functions, or error if they encounter unsupported functions.

Initialize

Before calling this function, you must call initialize with :ExprGraph.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+  49.0
source
MathOptInterface.objective_exprFunction
objective_expr(d::AbstractNLPEvaluator)::Expr

Returns a Julia Expr object representing the expression graph of the objective function.

Format

The expression has a number of limitations, compared with arbitrary Julia expressions:

  • All sums and products are flattened out as simple Expr(:+, ...) and Expr(:*, ...) objects.
  • All decision variables must be of the form Expr(:ref, :x, MOI.VariableIndex(i)), where i is the $i$th variable in ListOfVariableIndices.
  • There are currently no restrictions on recognized functions; typically these will be built-in Julia functions like ^, exp, log, cos, tan, sqrt, etc., but modeling interfaces may choose to extend these basic functions, or error if they encounter unsupported functions.

Initialize

Before calling this function, you must call initialize with :ExprGraph.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
 julia> MOI.initialize(evaluator, [:ExprGraph])
 
 julia> MOI.objective_expr(evaluator)
-:(x[MOI.VariableIndex(1)] * x[MOI.VariableIndex(4)] * (x[MOI.VariableIndex(1)] + x[MOI.VariableIndex(2)] + x[MOI.VariableIndex(3)]) + x[MOI.VariableIndex(3)])
source
MathOptInterface.constraint_exprFunction
constraint_expr(d::AbstractNLPEvaluator, i::Integer)::Expr

Returns a Julia Expr object representing the expression graph for the $i\text{th}$ nonlinear constraint.

Format

The format is the same as objective_expr, with an additional comparison operator indicating the sense of and bounds on the constraint.

For single-sided comparisons, the body of the constraint must be on the left-hand side, and the right-hand side must be a constant.

For double-sided comparisons (that is, $l \le g(x) \le u$), the body of the constraint must be in the middle, and the left- and right-hand sides must be constants.

The bounds on the constraints must match the NLPBoundsPairs passed to NLPBlockData.

Initialize

Before calling this function, you must call initialize with :ExprGraph.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
+:(x[MOI.VariableIndex(1)] * x[MOI.VariableIndex(4)] * (x[MOI.VariableIndex(1)] + x[MOI.VariableIndex(2)] + x[MOI.VariableIndex(3)]) + x[MOI.VariableIndex(3)])
source
MathOptInterface.constraint_exprFunction
constraint_expr(d::AbstractNLPEvaluator, i::Integer)::Expr

Returns a Julia Expr object representing the expression graph for the $i\text{th}$ nonlinear constraint.

Format

The format is the same as objective_expr, with an additional comparison operator indicating the sense of and bounds on the constraint.

For single-sided comparisons, the body of the constraint must be on the left-hand side, and the right-hand side must be a constant.

For double-sided comparisons (that is, $l \le g(x) \le u$), the body of the constraint must be in the middle, and the left- and right-hand sides must be constants.

The bounds on the constraints must match the NLPBoundsPairs passed to NLPBlockData.

Initialize

Before calling this function, you must call initialize with :ExprGraph.

Example

This example uses the Test.HS071 evaluator.

julia> import MathOptInterface as MOI
 
 julia> evaluator = MOI.Test.HS071(true);
 
@@ -421,4 +421,4 @@
 :(x[MOI.VariableIndex(1)] * x[MOI.VariableIndex(2)] * x[MOI.VariableIndex(3)] * x[MOI.VariableIndex(4)] >= 25.0)
 
 julia> MOI.constraint_expr(evaluator, 2)
-:(x[MOI.VariableIndex(1)] ^ 2 + x[MOI.VariableIndex(2)] ^ 2 + x[MOI.VariableIndex(3)] ^ 2 + x[MOI.VariableIndex(4)] ^ 2 == 40.0)
source
+:(x[MOI.VariableIndex(1)] ^ 2 + x[MOI.VariableIndex(2)] ^ 2 + x[MOI.VariableIndex(3)] ^ 2 + x[MOI.VariableIndex(4)] ^ 2 == 40.0)source diff --git a/dev/reference/standard_form/index.html b/dev/reference/standard_form/index.html index 4b981b28e2..8833629bb8 100644 --- a/dev/reference/standard_form/index.html +++ b/dev/reference/standard_form/index.html @@ -1,5 +1,5 @@ -Standard form · MathOptInterface

Standard form

Functions

MathOptInterface.AbstractFunctionType
AbstractFunction

Abstract supertype for function objects.

Required methods

All functions must implement:

Abstract subtypes of AbstractFunction may require additional methods to be implemented.

source
MathOptInterface.constantFunction
constant(f::AbstractFunction[, ::Type{T}]) where {T}

Returns the constant term of a scalar-valued function, or the constant vector of a vector-valued function.

If f is untyped and T is provided, returns zero(T).

source
constant(set::Union{EqualTo,GreaterThan,LessThan,Parameter})

Returns the constant term of the set set.

Example

julia> import MathOptInterface as MOI
+Standard form · MathOptInterface

Standard form

Functions

MathOptInterface.AbstractFunctionType
AbstractFunction

Abstract supertype for function objects.

Required methods

All functions must implement:

Abstract subtypes of AbstractFunction may require additional methods to be implemented.

source
MathOptInterface.constantFunction
constant(f::AbstractFunction[, ::Type{T}]) where {T}

Returns the constant term of a scalar-valued function, or the constant vector of a vector-valued function.

If f is untyped and T is provided, returns zero(T).

source
constant(set::Union{EqualTo,GreaterThan,LessThan,Parameter})

Returns the constant term of the set set.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.constant(MOI.GreaterThan(1.0))
 1.0
@@ -11,13 +11,13 @@
 3
 
 julia> MOI.constant(MOI.Parameter(4.5))
-4.5
source

Scalar functions

MathOptInterface.VariableIndexType
VariableIndex

A type-safe wrapper for Int64 for use in referencing variables in a model. To allow for deletion, indices need not be consecutive.

source
MathOptInterface.ScalarAffineTermType
ScalarAffineTerm{T}(coefficient::T, variable::VariableIndex) where {T}

Represents the scalar-valued term coefficient * variable.

Example

julia> import MathOptInterface as MOI
+4.5
source

Scalar functions

MathOptInterface.VariableIndexType
VariableIndex

A type-safe wrapper for Int64 for use in referencing variables in a model. To allow for deletion, indices need not be consecutive.

source
MathOptInterface.ScalarAffineTermType
ScalarAffineTerm{T}(coefficient::T, variable::VariableIndex) where {T}

Represents the scalar-valued term coefficient * variable.

Example

julia> import MathOptInterface as MOI
 
 julia> x = MOI.VariableIndex(1)
 MOI.VariableIndex(1)
 
 julia> MOI.ScalarAffineTerm(2.0, x)
-MathOptInterface.ScalarAffineTerm{Float64}(2.0, MOI.VariableIndex(1))
source
MathOptInterface.ScalarAffineFunctionType
ScalarAffineFunction{T}(
     terms::Vector{ScalarAffineTerm{T}},
     constant::T,
 ) where {T}

Represents the scalar-valued affine function $a^\top x + b$, where:

  • $a^\top x$ is represented by the vector of ScalarAffineTerms
  • $b$ is a scalar constant::T

Duplicates

Duplicate variable indices in terms are accepted, and the corresponding coefficients are summed together.

Example

julia> import MathOptInterface as MOI
@@ -31,7 +31,7 @@
  MathOptInterface.ScalarAffineTerm{Float64}(3.0, MOI.VariableIndex(1))
 
 julia> f = MOI.ScalarAffineFunction(terms, 4.0)
-4.0 + 2.0 MOI.VariableIndex(1) + 3.0 MOI.VariableIndex(1)
source
MathOptInterface.ScalarQuadraticTermType
ScalarQuadraticTerm{T}(
     coefficient::T,
     variable_1::VariableIndex,
     variable_2::VariableIndex,
@@ -41,7 +41,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.ScalarQuadraticTerm(2.0, x, x)
-MathOptInterface.ScalarQuadraticTerm{Float64}(2.0, MOI.VariableIndex(1), MOI.VariableIndex(1))
source
MathOptInterface.ScalarQuadraticFunctionType
ScalarQuadraticFunction{T}(
     quadratic_terms::Vector{ScalarQuadraticTerm{T}},
     affine_terms::Vector{ScalarAffineTerm{T}},
     constant::T,
@@ -64,7 +64,7 @@
  MathOptInterface.ScalarQuadraticTerm{Float64}(3.0, MOI.VariableIndex(1), MOI.VariableIndex(2))
 
 julia> f = MOI.ScalarQuadraticFunction(quadratic_terms, affine_terms, constant)
-5.0 + 4.0 MOI.VariableIndex(1) + 2.0 MOI.VariableIndex(1)² + 3.0 MOI.VariableIndex(1)*MOI.VariableIndex(2)
source
MathOptInterface.ScalarNonlinearFunctionType
ScalarNonlinearFunction(head::Symbol, args::Vector{Any})

The scalar-valued nonlinear function head(args...), represented as a symbolic expression tree, with the call operator head and ordered arguments in args.

head

The head::Symbol must be an operator supported by the model.

The default list of supported univariate operators is given by:

and the default list of supported multivariate operators is given by:

Additional operators can be registered by setting a UserDefinedFunction attribute.

See the full list of operators supported by a ModelLike by querying ListOfSupportedNonlinearOperators.

args

The vector args contains the arguments to the nonlinear function. If the operator is univariate, it must contain one element. Otherwise, it may contain multiple elements.

Each element must be one of the following:

Unsupported operators

If the optimizer does not support head, an UnsupportedNonlinearOperator error will be thrown.

There is no guarantee about when this error will be thrown; it may be thrown when the function is first added to the model, or it may be thrown when optimize! is called.

Example

To represent the function $f(x) = sin(x)^2$, do:

julia> import MathOptInterface as MOI
+5.0 + 4.0 MOI.VariableIndex(1) + 2.0 MOI.VariableIndex(1)² + 3.0 MOI.VariableIndex(1)*MOI.VariableIndex(2)
source
MathOptInterface.ScalarNonlinearFunctionType
ScalarNonlinearFunction(head::Symbol, args::Vector{Any})

The scalar-valued nonlinear function head(args...), represented as a symbolic expression tree, with the call operator head and ordered arguments in args.

head

The head::Symbol must be an operator supported by the model.

The default list of supported univariate operators is given by:

and the default list of supported multivariate operators is given by:

Additional operators can be registered by setting a UserDefinedFunction attribute.

See the full list of operators supported by a ModelLike by querying ListOfSupportedNonlinearOperators.

args

The vector args contains the arguments to the nonlinear function. If the operator is univariate, it must contain one element. Otherwise, it may contain multiple elements.

Each element must be one of the following:

Unsupported operators

If the optimizer does not support head, an UnsupportedNonlinearOperator error will be thrown.

There is no guarantee about when this error will be thrown; it may be thrown when the function is first added to the model, or it may be thrown when optimize! is called.

Example

To represent the function $f(x) = sin(x)^2$, do:

julia> import MathOptInterface as MOI
 
 julia> x = MOI.VariableIndex(1)
 MOI.VariableIndex(1)
@@ -73,7 +73,7 @@
            :^,
            Any[MOI.ScalarNonlinearFunction(:sin, Any[x]), 2],
        )
-^(sin(MOI.VariableIndex(1)), (2))
source

Vector functions

MathOptInterface.VectorOfVariablesType
VectorOfVariables(variables::Vector{VariableIndex}) <: AbstractVectorFunction

The vector-valued function f(x) = variables, where variables is a subset of VariableIndexes in the model.

The list of variables may contain duplicates.

Example

julia> import MathOptInterface as MOI
+^(sin(MOI.VariableIndex(1)), (2))
source

Vector functions

MathOptInterface.VectorOfVariablesType
VectorOfVariables(variables::Vector{VariableIndex}) <: AbstractVectorFunction

The vector-valued function f(x) = variables, where variables is a subset of VariableIndexes in the model.

The list of variables may contain duplicates.

Example

julia> import MathOptInterface as MOI
 
 julia> x = MOI.VariableIndex.(1:2)
 2-element Vector{MathOptInterface.VariableIndex}:
@@ -88,7 +88,7 @@
 └                    ┘
 
 julia> MOI.output_dimension(f)
-3
source
MathOptInterface.VectorAffineTermType
VectorAffineTerm{T}(
     output_index::Int64,
     scalar_term::ScalarAffineTerm{T},
 ) where {T}

A VectorAffineTerm is a scalar_term that appears in the output_index row of the vector-valued VectorAffineFunction or VectorQuadraticFunction.

Example

julia> import MathOptInterface as MOI
@@ -96,7 +96,7 @@
 julia> x = MOI.VariableIndex(1);
 
 julia> MOI.VectorAffineTerm(Int64(2), MOI.ScalarAffineTerm(3.0, x))
-MathOptInterface.VectorAffineTerm{Float64}(2, MathOptInterface.ScalarAffineTerm{Float64}(3.0, MOI.VariableIndex(1)))
source
MathOptInterface.VectorAffineFunctionType
VectorAffineFunction{T}(
     terms::Vector{VectorAffineTerm{T}},
     constants::Vector{T},
 ) where {T}

The vector-valued affine function $A x + b$, where:

  • $A x$ is the sparse matrix given by the vector of VectorAffineTerms
  • $b$ is the vector constants

Duplicates

Duplicate indices in the $A$ are accepted, and the corresponding coefficients are summed together.

Example

julia> import MathOptInterface as MOI
@@ -115,7 +115,7 @@
 └                              ┘
 
 julia> MOI.output_dimension(f)
-2
source
MathOptInterface.VectorQuadraticTermType
VectorQuadraticTerm{T}(
     output_index::Int64,
     scalar_term::ScalarQuadraticTerm{T},
 ) where {T}

A VectorQuadraticTerm is a ScalarQuadraticTerm scalar_term that appears in the output_index row of the vector-valued VectorQuadraticFunction.

Example

julia> import MathOptInterface as MOI
@@ -123,7 +123,7 @@
 julia> x = MOI.VariableIndex(1);
 
 julia> MOI.VectorQuadraticTerm(Int64(2), MOI.ScalarQuadraticTerm(3.0, x, x))
-MathOptInterface.VectorQuadraticTerm{Float64}(2, MathOptInterface.ScalarQuadraticTerm{Float64}(3.0, MOI.VariableIndex(1), MOI.VariableIndex(1)))
source
MathOptInterface.VectorQuadraticFunctionType
VectorQuadraticFunction{T}(
     quadratic_terms::Vector{VectorQuadraticTerm{T}},
     affine_terms::Vector{VectorAffineTerm{T}},
     constants::Vector{T},
@@ -152,7 +152,7 @@
 └                                                                              ┘
 
 julia> MOI.output_dimension(f)
-2
source
MathOptInterface.VectorNonlinearFunctionType
VectorNonlinearFunction(args::Vector{ScalarNonlinearFunction})

The vector-valued nonlinear function composed of a vector of ScalarNonlinearFunction.

args

The vector args contains the scalar elements of the nonlinear function. Each element must be a ScalarNonlinearFunction, but if you pass a Vector{Any}, the elements can be automatically converted from one of the following:

Example

To represent the function $f(x) = [sin(x)^2, x]$, do:

julia> import MathOptInterface as MOI
+2
source
MathOptInterface.VectorNonlinearFunctionType
VectorNonlinearFunction(args::Vector{ScalarNonlinearFunction})

The vector-valued nonlinear function composed of a vector of ScalarNonlinearFunction.

args

The vector args contains the scalar elements of the nonlinear function. Each element must be a ScalarNonlinearFunction, but if you pass a Vector{Any}, the elements can be automatically converted from one of the following:

Example

To represent the function $f(x) = [sin(x)^2, x]$, do:

julia> import MathOptInterface as MOI
 
 julia> x = MOI.VariableIndex(1)
 MOI.VariableIndex(1)
@@ -167,7 +167,7 @@
 ┌                                 ┐
 │^(sin(MOI.VariableIndex(1)), 2.0)│
 │+(MOI.VariableIndex(1))          │
-└                                 ┘

Note the automatic conversion from x to +(x).

source

Sets

MathOptInterface.AbstractSetType
AbstractSet

Abstract supertype for set objects used to encode constraints.

Required methods

For sets of type S with isbitstype(S) == false, you must implement:

  • Base.copy(set::S)
  • Base.:(==)(x::S, y::S)

Subtypes of AbstractSet such as AbstractScalarSet and AbstractVectorSet may prescribe additional required methods.

Optional methods

You may optionally implement:

Note for developers

When creating a new set, the set struct must not contain any VariableIndex or ConstraintIndex objects.

source
MathOptInterface.AbstractVectorSetType
AbstractVectorSet

Abstract supertype for subsets of $\mathbb{R}^n$ for some $n$.

Required methods

All AbstractVectorSets of type S must implement:

  • dimension, unless the dimension is stored in the set.dimension field
  • Utilities.set_dot, unless the dot product between two vectors in the set is equivalent to LinearAlgebra.dot.
source

Utilities

Sets

MathOptInterface.AbstractSetType
AbstractSet

Abstract supertype for set objects used to encode constraints.

Required methods

For sets of type S with isbitstype(S) == false, you must implement:

  • Base.copy(set::S)
  • Base.:(==)(x::S, y::S)

Subtypes of AbstractSet such as AbstractScalarSet and AbstractVectorSet may prescribe additional required methods.

Optional methods

You may optionally implement:

Note for developers

When creating a new set, the set struct must not contain any VariableIndex or ConstraintIndex objects.

source
MathOptInterface.AbstractVectorSetType
AbstractVectorSet

Abstract supertype for subsets of $\mathbb{R}^n$ for some $n$.

Required methods

All AbstractVectorSets of type S must implement:

  • dimension, unless the dimension is stored in the set.dimension field
  • Utilities.set_dot, unless the dot product between two vectors in the set is equivalent to LinearAlgebra.dot.
source

Utilities

MathOptInterface.dimensionFunction
dimension(set::AbstractSet)

Return the output_dimension that an AbstractFunction should have to be used with the set set.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.dimension(MOI.Reals(4))
 4
@@ -176,7 +176,7 @@
 1
 
 julia> MOI.dimension(MOI.PositiveSemidefiniteConeTriangle(2))
-3
source
MathOptInterface.dual_setFunction
dual_set(set::AbstractSet)

Return the dual set of set, that is the dual cone of the set. This follows the definition of duality discussed in Duality.

See Dual cone for more information.

If the dual cone is not defined it returns an error.

Example

julia> import MathOptInterface as MOI
+3
source
MathOptInterface.dual_setFunction
dual_set(set::AbstractSet)

Return the dual set of set, that is the dual cone of the set. This follows the definition of duality discussed in Duality.

See Dual cone for more information.

If the dual cone is not defined it returns an error.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.dual_set(MOI.Reals(4))
 MathOptInterface.Zeros(4)
@@ -185,7 +185,7 @@
 MathOptInterface.SecondOrderCone(5)
 
 julia> MOI.dual_set(MOI.ExponentialCone())
-MathOptInterface.DualExponentialCone()
source
MathOptInterface.dual_set_typeFunction
dual_set_type(S::Type{<:AbstractSet})

Return the type of dual set of sets of type S, as returned by dual_set. If the dual cone is not defined it returns an error.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.DualExponentialCone()
source
MathOptInterface.dual_set_typeFunction
dual_set_type(S::Type{<:AbstractSet})

Return the type of dual set of sets of type S, as returned by dual_set. If the dual cone is not defined it returns an error.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.dual_set_type(MOI.Reals)
 MathOptInterface.Zeros
@@ -194,7 +194,7 @@
 MathOptInterface.SecondOrderCone
 
 julia> MOI.dual_set_type(MOI.ExponentialCone)
-MathOptInterface.DualExponentialCone
source
MathOptInterface.constantMethod
constant(set::Union{EqualTo,GreaterThan,LessThan,Parameter})

Returns the constant term of the set set.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.DualExponentialCone
source
MathOptInterface.constantMethod
constant(set::Union{EqualTo,GreaterThan,LessThan,Parameter})

Returns the constant term of the set set.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.constant(MOI.GreaterThan(1.0))
 1.0
@@ -206,7 +206,7 @@
 3
 
 julia> MOI.constant(MOI.Parameter(4.5))
-4.5
source
MathOptInterface.supports_dimension_updateFunction
supports_dimension_update(S::Type{<:MOI.AbstractVectorSet})

Return a Bool indicating whether the elimination of any dimension of n-dimensional sets of type S give an n-1-dimensional set S. By default, this function returns false so it should only be implemented for sets that supports dimension update.

For instance, supports_dimension_update(MOI.Nonnegatives) is true because the elimination of any dimension of the n-dimensional nonnegative orthant gives the n-1-dimensional nonnegative orthant. However supports_dimension_update(MOI.ExponentialCone) is false.

source

Scalar sets

List of recognized scalar sets.

MathOptInterface.supports_dimension_updateFunction
supports_dimension_update(S::Type{<:MOI.AbstractVectorSet})

Return a Bool indicating whether the elimination of any dimension of n-dimensional sets of type S give an n-1-dimensional set S. By default, this function returns false so it should only be implemented for sets that supports dimension update.

For instance, supports_dimension_update(MOI.Nonnegatives) is true because the elimination of any dimension of the n-dimensional nonnegative orthant gives the n-1-dimensional nonnegative orthant. However supports_dimension_update(MOI.ExponentialCone) is false.

source

Scalar sets

List of recognized scalar sets.

MathOptInterface.GreaterThanType
GreaterThan{T<:Real}(lower::T)

The set $[lower, \infty) \subseteq \mathbb{R}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -214,7 +214,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.GreaterThan(0.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(1)
source
MathOptInterface.LessThanType
LessThan{T<:Real}(upper::T)

The set $(-\infty, upper] \subseteq \mathbb{R}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.GreaterThan{Float64}}(1)
source
MathOptInterface.LessThanType
LessThan{T<:Real}(upper::T)

The set $(-\infty, upper] \subseteq \mathbb{R}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -222,7 +222,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.LessThan(2.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.LessThan{Float64}}(1)
source
MathOptInterface.EqualToType
EqualTo{T<:Number}(value::T)

The set containing the single point $\{value\} \subseteq \mathbb{R}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.LessThan{Float64}}(1)
source
MathOptInterface.EqualToType
EqualTo{T<:Number}(value::T)

The set containing the single point $\{value\} \subseteq \mathbb{R}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -230,7 +230,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.EqualTo(2.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.EqualTo{Float64}}(1)
source
MathOptInterface.IntervalType
Interval{T<:Real}(lower::T, upper::T)

The interval $[lower, upper] \subseteq \mathbb{R} \cup \{-\infty, +\infty\}$.

If lower or upper is -Inf or Inf, respectively, the set is interpreted as a one-sided interval.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.EqualTo{Float64}}(1)
source
MathOptInterface.IntervalType
Interval{T<:Real}(lower::T, upper::T)

The interval $[lower, upper] \subseteq \mathbb{R} \cup \{-\infty, +\infty\}$.

If lower or upper is -Inf or Inf, respectively, the set is interpreted as a one-sided interval.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -238,7 +238,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.Interval(1.0, 2.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}}(1)
source
MathOptInterface.IntegerType
Integer()

The set of integers, $\mathbb{Z}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}}(1)
source
MathOptInterface.IntegerType
Integer()

The set of integers, $\mathbb{Z}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -246,7 +246,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.Integer())
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Integer}(1)
source
MathOptInterface.ZeroOneType
ZeroOne()

The set $\{0, 1\}$.

Variables belonging to the ZeroOne set are also known as "binary" variables.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Integer}(1)
source
MathOptInterface.ZeroOneType
ZeroOne()

The set $\{0, 1\}$.

Variables belonging to the ZeroOne set are also known as "binary" variables.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -254,7 +254,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.ZeroOne())
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.ZeroOne}(1)
source
MathOptInterface.SemicontinuousType
Semicontinuous{T<:Real}(lower::T, upper::T)

The set $\{0\} \cup [lower, upper]$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.ZeroOne}(1)
source
MathOptInterface.SemicontinuousType
Semicontinuous{T<:Real}(lower::T, upper::T)

The set $\{0\} \cup [lower, upper]$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -262,7 +262,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.Semicontinuous(2.0, 3.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Semicontinuous{Float64}}(1)
source
MathOptInterface.SemiintegerType
Semiinteger{T<:Real}(lower::T, upper::T)

The set $\{0\} \cup \{lower, lower+1, \ldots, upper-1, upper\}$.

Note that if lower and upper are not equivalent to an integer, then the solver may throw an error, or it may round up lower and round down upper to the nearest integers.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Semicontinuous{Float64}}(1)
source
MathOptInterface.SemiintegerType
Semiinteger{T<:Real}(lower::T, upper::T)

The set $\{0\} \cup \{lower, lower+1, \ldots, upper-1, upper\}$.

Note that if lower and upper are not equivalent to an integer, then the solver may throw an error, or it may round up lower and round down upper to the nearest integers.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -270,7 +270,7 @@
 MOI.VariableIndex(1)
 
 julia> MOI.add_constraint(model, x, MOI.Semiinteger(2.0, 3.0))
-MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Semiinteger{Float64}}(1)
source
MathOptInterface.ParameterType
Parameter{T<:Number}(value::T)

The set containing the single point $\{value\} \subseteq \mathbb{R}$.

The Parameter set is conceptually similar to the EqualTo set, except that a variable constrained to the Parameter set cannot have other constraints added to it, and the Parameter set can never be deleted. Thus, solvers are free to treat the variable as a constant, and they need not add it as a decision variable to the model.

Because of this behavior, you must add parameters using add_constrained_variable, and solvers should declare supports_add_constrained_variable and not supports_constraint for the Parameter set.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Semiinteger{Float64}}(1)
source
MathOptInterface.ParameterType
Parameter{T<:Number}(value::T)

The set containing the single point $\{value\} \subseteq \mathbb{R}$.

The Parameter set is conceptually similar to the EqualTo set, except that a variable constrained to the Parameter set cannot have other constraints added to it, and the Parameter set can never be deleted. Thus, solvers are free to treat the variable as a constant, and they need not add it as a decision variable to the model.

Because of this behavior, you must add parameters using add_constrained_variable, and solvers should declare supports_add_constrained_variable and not supports_constraint for the Parameter set.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -280,35 +280,35 @@
 julia> MOI.set(model, MOI.ConstraintSet(), ci, MOI.Parameter(3.0))
 
 julia> MOI.get(model, MOI.ConstraintSet(), ci)
-MathOptInterface.Parameter{Float64}(3.0)
source

Vector sets

List of recognized vector sets.

MathOptInterface.RealsType
Reals(dimension::Int)

The set $\mathbb{R}^{dimension}$ (containing all points) of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.Parameter{Float64}(3.0)
source

Vector sets

List of recognized vector sets.

MathOptInterface.RealsType
Reals(dimension::Int)

The set $\mathbb{R}^{dimension}$ (containing all points) of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Reals(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Reals}(1)
source
MathOptInterface.ZerosType
Zeros(dimension::Int)

The set $\{ 0 \}^{dimension}$ (containing only the origin) of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Reals}(1)
source
MathOptInterface.ZerosType
Zeros(dimension::Int)

The set $\{ 0 \}^{dimension}$ (containing only the origin) of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Zeros(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Zeros}(1)
source
MathOptInterface.NonnegativesType
Nonnegatives(dimension::Int)

The nonnegative orthant $\{ x \in \mathbb{R}^{dimension} : x \ge 0 \}$ of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Zeros}(1)
source
MathOptInterface.NonnegativesType
Nonnegatives(dimension::Int)

The nonnegative orthant $\{ x \in \mathbb{R}^{dimension} : x \ge 0 \}$ of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Nonnegatives(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Nonnegatives}(1)
source
MathOptInterface.NonpositivesType
Nonpositives(dimension::Int)

The nonpositive orthant $\{ x \in \mathbb{R}^{dimension} : x \le 0 \}$ of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Nonnegatives}(1)
source
MathOptInterface.NonpositivesType
Nonpositives(dimension::Int)

The nonpositive orthant $\{ x \in \mathbb{R}^{dimension} : x \le 0 \}$ of non-negative dimension dimension.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Nonpositives(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Nonpositives}(1)
source
MathOptInterface.NormInfinityConeType
NormInfinityCone(dimension::Int)

The $\ell_\infty$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_\infty = \max_i \lvert x_i \rvert \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Nonpositives}(1)
source
MathOptInterface.NormInfinityConeType
NormInfinityCone(dimension::Int)

The $\ell_\infty$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_\infty = \max_i \lvert x_i \rvert \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -318,7 +318,7 @@
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([t; x]), MOI.NormInfinityCone(4))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormInfinityCone}(1)
source
MathOptInterface.NormOneConeType
NormOneCone(dimension::Int)

The $\ell_1$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_1 = \sum_i \lvert x_i \rvert \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormInfinityCone}(1)
source
MathOptInterface.NormOneConeType
NormOneCone(dimension::Int)

The $\ell_1$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_1 = \sum_i \lvert x_i \rvert \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -328,7 +328,7 @@
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([t; x]), MOI.NormOneCone(4))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormOneCone}(1)
source
MathOptInterface.NormConeType
NormCone(p::Float64, dimension::Int)

The $\ell_p$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \left(\sum\limits_i |x_i|^p\right)^{\frac{1}{p}} \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormOneCone}(1)
source
MathOptInterface.NormConeType
NormCone(p::Float64, dimension::Int)

The $\ell_p$-norm cone $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \left(\sum\limits_i |x_i|^p\right)^{\frac{1}{p}} \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -338,7 +338,7 @@
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([t; x]), MOI.NormCone(3, 4))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormCone}(1)
source
MathOptInterface.SecondOrderConeType
SecondOrderCone(dimension::Int)

The second-order cone (or Lorenz cone or $\ell_2$-norm cone) $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_2 \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormCone}(1)
source
MathOptInterface.SecondOrderConeType
SecondOrderCone(dimension::Int)

The second-order cone (or Lorenz cone or $\ell_2$-norm cone) $\{ (t,x) \in \mathbb{R}^{dimension} : t \ge \lVert x \rVert_2 \}$ of dimension dimension.

The dimension must be at least 1.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -348,7 +348,7 @@
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([t; x]), MOI.SecondOrderCone(4))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SecondOrderCone}(1)
source
MathOptInterface.RotatedSecondOrderConeType
RotatedSecondOrderCone(dimension::Int)

The rotated second-order cone $\{ (t,u,x) \in \mathbb{R}^{dimension} : 2tu \ge \lVert x \rVert_2^2, t,u \ge 0 \}$ of dimension dimension.

The dimension must be at least 2.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SecondOrderCone}(1)
source
MathOptInterface.RotatedSecondOrderConeType
RotatedSecondOrderCone(dimension::Int)

The rotated second-order cone $\{ (t,u,x) \in \mathbb{R}^{dimension} : 2tu \ge \lVert x \rVert_2^2, t,u \ge 0 \}$ of dimension dimension.

The dimension must be at least 2.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -365,7 +365,7 @@
            MOI.VectorOfVariables([t; u; x]),
            MOI.RotatedSecondOrderCone(5),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RotatedSecondOrderCone}(1)
source
MathOptInterface.GeometricMeanConeType
GeometricMeanCone(dimension::Int)

The geometric mean cone $\{ (t,x) \in \mathbb{R}^{n+1} : x \ge 0, t \le \sqrt[n]{x_1 x_2 \cdots x_n} \}$, where dimension = n + 1 >= 2.

Duality note

The dual of the geometric mean cone is $\{ (u, v) \in \mathbb{R}^{n+1} : u \le 0, v \ge 0, -u \le n \sqrt[n]{\prod_i v_i} \}$, where dimension = n + 1 >= 2.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RotatedSecondOrderCone}(1)
source
MathOptInterface.GeometricMeanConeType
GeometricMeanCone(dimension::Int)

The geometric mean cone $\{ (t,x) \in \mathbb{R}^{n+1} : x \ge 0, t \le \sqrt[n]{x_1 x_2 \cdots x_n} \}$, where dimension = n + 1 >= 2.

Duality note

The dual of the geometric mean cone is $\{ (u, v) \in \mathbb{R}^{n+1} : u \le 0, v \ge 0, -u \le n \sqrt[n]{\prod_i v_i} \}$, where dimension = n + 1 >= 2.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -379,35 +379,35 @@
            MOI.VectorOfVariables([t; x]),
            MOI.GeometricMeanCone(4),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.GeometricMeanCone}(1)
source
MathOptInterface.ExponentialConeType
ExponentialCone()

The 3-dimensional exponential cone $\{ (x,y,z) \in \mathbb{R}^3 : y \exp (x/y) \le z, y > 0 \}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.GeometricMeanCone}(1)
source
MathOptInterface.ExponentialConeType
ExponentialCone()

The 3-dimensional exponential cone $\{ (x,y,z) \in \mathbb{R}^3 : y \exp (x/y) \le z, y > 0 \}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.ExponentialCone())
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.ExponentialCone}(1)
source
MathOptInterface.DualExponentialConeType
DualExponentialCone()

The 3-dimensional dual exponential cone $\{ (u,v,w) \in \mathbb{R}^3 : -u \exp (v/u) \le \exp(1) w, u < 0 \}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.ExponentialCone}(1)
source
MathOptInterface.DualExponentialConeType
DualExponentialCone()

The 3-dimensional dual exponential cone $\{ (u,v,w) \in \mathbb{R}^3 : -u \exp (v/u) \le \exp(1) w, u < 0 \}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.DualExponentialCone())
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.DualExponentialCone}(1)
source
MathOptInterface.PowerConeType
PowerCone{T<:Real}(exponent::T)

The 3-dimensional power cone $\{ (x,y,z) \in \mathbb{R}^3 : x^{exponent} y^{1-exponent} \ge |z|, x \ge 0, y \ge 0 \}$ with parameter exponent.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.DualExponentialCone}(1)
source
MathOptInterface.PowerConeType
PowerCone{T<:Real}(exponent::T)

The 3-dimensional power cone $\{ (x,y,z) \in \mathbb{R}^3 : x^{exponent} y^{1-exponent} \ge |z|, x \ge 0, y \ge 0 \}$ with parameter exponent.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.PowerCone(0.5))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.PowerCone{Float64}}(1)
source
MathOptInterface.DualPowerConeType
DualPowerCone{T<:Real}(exponent::T)

The 3-dimensional power cone $\{ (u,v,w) \in \mathbb{R}^3 : (\frac{u}{exponent})^{exponent} (\frac{v}{1-exponent})^{1-exponent} \ge |w|, u \ge 0, v \ge 0 \}$ with parameter exponent.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.PowerCone{Float64}}(1)
source
MathOptInterface.DualPowerConeType
DualPowerCone{T<:Real}(exponent::T)

The 3-dimensional power cone $\{ (u,v,w) \in \mathbb{R}^3 : (\frac{u}{exponent})^{exponent} (\frac{v}{1-exponent})^{1-exponent} \ge |w|, u \ge 0, v \ge 0 \}$ with parameter exponent.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variables(model, 3);
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.DualPowerCone(0.5))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.DualPowerCone{Float64}}(1)
source
MathOptInterface.RelativeEntropyConeType
RelativeEntropyCone(dimension::Int)

The relative entropy cone $\{ (u, v, w) \in \mathbb{R}^{1+2n} : u \ge \sum_{i=1}^n w_i \log(\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}$, where dimension = 2n + 1 >= 1.

Duality note

The dual of the relative entropy cone is $\{ (u, v, w) \in \mathbb{R}^{1+2n} : \forall i, w_i \ge u (\log (\frac{u}{v_i}) - 1), v_i \ge 0, u > 0 \}$ of dimension dimension${}=2n+1$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.DualPowerCone{Float64}}(1)
source
MathOptInterface.RelativeEntropyConeType
RelativeEntropyCone(dimension::Int)

The relative entropy cone $\{ (u, v, w) \in \mathbb{R}^{1+2n} : u \ge \sum_{i=1}^n w_i \log(\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}$, where dimension = 2n + 1 >= 1.

Duality note

The dual of the relative entropy cone is $\{ (u, v, w) \in \mathbb{R}^{1+2n} : \forall i, w_i \ge u (\log (\frac{u}{v_i}) - 1), v_i \ge 0, u > 0 \}$ of dimension dimension${}=2n+1$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -422,7 +422,7 @@
            MOI.VectorOfVariables([u; v; w]),
            MOI.RelativeEntropyCone(7),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RelativeEntropyCone}(1)
source
MathOptInterface.NormSpectralConeType
NormSpectralCone(row_dim::Int, column_dim::Int)

The epigraph of the matrix spectral norm (maximum singular value function) $\{ (t, X) \in \mathbb{R}^{1 + row_dim \times column_dim} : t \ge \sigma_1(X) \}$, where $\sigma_i$ is the $i$th singular value of the matrix $X$ of non-negative row dimension row_dim and column dimension column_dim.

The matrix X is vectorized by stacking the columns, matching the behavior of Julia's vec function.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RelativeEntropyCone}(1)
source
MathOptInterface.NormSpectralConeType
NormSpectralCone(row_dim::Int, column_dim::Int)

The epigraph of the matrix spectral norm (maximum singular value function) $\{ (t, X) \in \mathbb{R}^{1 + row_dim \times column_dim} : t \ge \sigma_1(X) \}$, where $\sigma_i$ is the $i$th singular value of the matrix $X$ of non-negative row dimension row_dim and column dimension column_dim.

The matrix X is vectorized by stacking the columns, matching the behavior of Julia's vec function.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -439,7 +439,7 @@
            MOI.VectorOfVariables([t; vec(X)]),
            MOI.NormSpectralCone(2, 3),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormSpectralCone}(1)
source
MathOptInterface.NormNuclearConeType
NormNuclearCone(row_dim::Int, column_dim::Int)

The epigraph of the matrix nuclear norm (sum of singular values function) $\{ (t, X) \in \mathbb{R}^{1 + row_dim \times column_dim} : t \ge \sum_i \sigma_i(X) \}$, where $\sigma_i$ is the $i$th singular value of the matrix $X$ of non-negative row dimension row_dim and column dimension column_dim.

The matrix X is vectorized by stacking the columns, matching the behavior of Julia's vec function.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormSpectralCone}(1)
source
MathOptInterface.NormNuclearConeType
NormNuclearCone(row_dim::Int, column_dim::Int)

The epigraph of the matrix nuclear norm (sum of singular values function) $\{ (t, X) \in \mathbb{R}^{1 + row_dim \times column_dim} : t \ge \sum_i \sigma_i(X) \}$, where $\sigma_i$ is the $i$th singular value of the matrix $X$ of non-negative row dimension row_dim and column dimension column_dim.

The matrix X is vectorized by stacking the columns, matching the behavior of Julia's vec function.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -456,7 +456,7 @@
            MOI.VectorOfVariables([t; vec(X)]),
            MOI.NormNuclearCone(2, 3),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormNuclearCone}(1)
source
MathOptInterface.SOS1Type
SOS1{T<:Real}(weights::Vector{T})

The set corresponding to the Special Ordered Set (SOS) constraint of Type I.

Of the variables in the set, at most one can be nonzero.

The weights induce an ordering of the variables such that the kth element in the set corresponds to the kth weight in weights. Solvers may use these weights to improve the efficiency of the solution process, but the ordering does not change the set of feasible solutions.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.NormNuclearCone}(1)
source
MathOptInterface.SOS1Type
SOS1{T<:Real}(weights::Vector{T})

The set corresponding to the Special Ordered Set (SOS) constraint of Type I.

Of the variables in the set, at most one can be nonzero.

The weights induce an ordering of the variables such that the kth element in the set corresponds to the kth weight in weights. Solvers may use these weights to improve the efficiency of the solution process, but the ordering does not change the set of feasible solutions.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -467,7 +467,7 @@
            MOI.VectorOfVariables(x),
            MOI.SOS1([1.0, 3.0, 2.5]),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SOS1{Float64}}(1)
source
MathOptInterface.SOS2Type
SOS2{T<:Real}(weights::Vector{T})

The set corresponding to the Special Ordered Set (SOS) constraint of Type II.

The weights induce an ordering of the variables such that the kth element in the set corresponds to the kth weight in weights. Therefore, the weights must be unique.

Of the variables in the set, at most two can be nonzero, and if two are nonzero, they must be adjacent in the ordering of the set.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SOS1{Float64}}(1)
source
MathOptInterface.SOS2Type
SOS2{T<:Real}(weights::Vector{T})

The set corresponding to the Special Ordered Set (SOS) constraint of Type II.

The weights induce an ordering of the variables such that the kth element in the set corresponds to the kth weight in weights. Therefore, the weights must be unique.

Of the variables in the set, at most two can be nonzero, and if two are nonzero, they must be adjacent in the ordering of the set.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -478,7 +478,7 @@
            MOI.VectorOfVariables(x),
            MOI.SOS2([1.0, 3.0, 2.5]),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SOS2{Float64}}(1)
source
MathOptInterface.IndicatorType
Indicator{A<:ActivationCondition,S<:AbstractScalarSet}(set::S)

The set corresponding to an indicator constraint.

When A is ACTIVATE_ON_ZERO, this means: $\{(y, x) \in \{0, 1\} \times \mathbb{R}^n : y = 0 \implies x \in set\}$

When A is ACTIVATE_ON_ONE, this means: $\{(y, x) \in \{0, 1\} \times \mathbb{R}^n : y = 1 \implies x \in set\}$

Notes

Most solvers expect that the first row of the function is interpretable as a variable index x_i (for example, 1.0 * x + 0.0). An error will be thrown if this is not the case.

Example

The constraint $\{(y, x) \in \{0, 1\} \times \mathbb{R}^2 : y = 1 \implies x_1 + x_2 \leq 9 \}$ is defined as

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.SOS2{Float64}}(1)
source
MathOptInterface.IndicatorType
Indicator{A<:ActivationCondition,S<:AbstractScalarSet}(set::S)

The set corresponding to an indicator constraint.

When A is ACTIVATE_ON_ZERO, this means: $\{(y, x) \in \{0, 1\} \times \mathbb{R}^n : y = 0 \implies x \in set\}$

When A is ACTIVATE_ON_ONE, this means: $\{(y, x) \in \{0, 1\} \times \mathbb{R}^n : y = 1 \implies x \in set\}$

Notes

Most solvers expect that the first row of the function is interpretable as a variable index x_i (for example, 1.0 * x + 0.0). An error will be thrown if this is not the case.

Example

The constraint $\{(y, x) \in \{0, 1\} \times \mathbb{R}^2 : y = 1 \implies x_1 + x_2 \leq 9 \}$ is defined as

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -507,7 +507,7 @@
 MathOptInterface.Indicator{MathOptInterface.ACTIVATE_ON_ONE, MathOptInterface.LessThan{Float64}}(MathOptInterface.LessThan{Float64}(9.0))
 
 julia> MOI.add_constraint(model, f, s)
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Indicator{MathOptInterface.ACTIVATE_ON_ONE, MathOptInterface.LessThan{Float64}}}(1)
source
MathOptInterface.ComplementsType
Complements(dimension::Base.Integer)

The set corresponding to a mixed complementarity constraint.

Complementarity constraints should be specified with an AbstractVectorFunction-in-Complements(dimension) constraint.

The dimension of the vector-valued function F must be dimension. This defines a complementarity constraint between the scalar function F[i] and the variable in F[i + dimension/2]. Thus, F[i + dimension/2] must be interpretable as a single variable x_i (for example, 1.0 * x + 0.0), and dimension must be even.

The mixed complementarity problem consists of finding x_i in the interval [lb, ub] (that is, in the set Interval(lb, ub)), such that the following holds:

  1. F_i(x) == 0 if lb_i < x_i < ub_i
  2. F_i(x) >= 0 if lb_i == x_i
  3. F_i(x) <= 0 if x_i == ub_i

Classically, the bounding set for x_i is Interval(0, Inf), which recovers: 0 <= F_i(x) ⟂ x_i >= 0, where the operator implies F_i(x) * x_i = 0.

Example

The problem:

x -in- Interval(-1, 1)
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Indicator{MathOptInterface.ACTIVATE_ON_ONE, MathOptInterface.LessThan{Float64}}}(1)
source
MathOptInterface.ComplementsType
Complements(dimension::Base.Integer)

The set corresponding to a mixed complementarity constraint.

Complementarity constraints should be specified with an AbstractVectorFunction-in-Complements(dimension) constraint.

The dimension of the vector-valued function F must be dimension. This defines a complementarity constraint between the scalar function F[i] and the variable in F[i + dimension/2]. Thus, F[i + dimension/2] must be interpretable as a single variable x_i (for example, 1.0 * x + 0.0), and dimension must be even.

The mixed complementarity problem consists of finding x_i in the interval [lb, ub] (that is, in the set Interval(lb, ub)), such that the following holds:

  1. F_i(x) == 0 if lb_i < x_i < ub_i
  2. F_i(x) >= 0 if lb_i == x_i
  3. F_i(x) <= 0 if x_i == ub_i

Classically, the bounding set for x_i is Interval(0, Inf), which recovers: 0 <= F_i(x) ⟂ x_i >= 0, where the operator implies F_i(x) * x_i = 0.

Example

The problem:

x -in- Interval(-1, 1)
 [-4 * x - 3, x] -in- Complements(2)

defines the mixed complementarity problem where the following holds:

  1. -4 * x - 3 == 0 if -1 < x < 1
  2. -4 * x - 3 >= 0 if x == -1
  3. -4 * x - 3 <= 0 if x == 1

There are three solutions:

  1. x = -3/4 with F(x) = 0
  2. x = -1 with F(x) = 1
  3. x = 1 with F(x) = -7
julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
@@ -534,7 +534,7 @@
             MOI.VectorOfVariables(x),
             MOI.Complements(4),
         )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Complements}(1)
source
MathOptInterface.HyperRectangleType
HyperRectangle(lower::Vector{T}, upper::Vector{T}) where {T}

The set $\{x \in \bar{\mathbb{R}}^d: x_i \in [lower_i, upper_i] \forall i=1,\ldots,d\}$.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Complements}(1)
source
MathOptInterface.HyperRectangleType
HyperRectangle(lower::Vector{T}, upper::Vector{T}) where {T}

The set $\{x \in \bar{\mathbb{R}}^d: x_i \in [lower_i, upper_i] \forall i=1,\ldots,d\}$.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -549,7 +549,7 @@
            MOI.VectorOfVariables(x),
            MOI.HyperRectangle(zeros(3), ones(3)),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.HyperRectangle{Float64}}(1)
source
MathOptInterface.ScaledType
struct Scaled{S<:AbstractVectorSet} <: AbstractVectorSet
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.HyperRectangle{Float64}}(1)
source
MathOptInterface.ScaledType
struct Scaled{S<:AbstractVectorSet} <: AbstractVectorSet
     set::S
 end

Given a vector $a \in \mathbb{R}^d$ and a set representing the set $\mathcal{S} \in \mathbb{R}^d$ such that Utilities.set_dot for $x \in \mathcal{S}$ and $y \in \mathcal{S}^*$ is

\[\sum_{i=1}^d a_i x_i y_i\]

the set Scaled(set) is defined as

\[\{ (\sqrt{a_1} x_1, \sqrt{a_2} x_2, \ldots, \sqrt{a_d} x_d) : x \in S \}\]

Example

This can be used to scale a vector of numbers

julia> import MathOptInterface as MOI
 
@@ -587,7 +587,7 @@
 │0.0 + 1.0 MOI.VariableIndex(1)               │
 │0.0 + 1.4142135623730951 MOI.VariableIndex(2)│
 │0.0 + 1.0 MOI.VariableIndex(3)               │
-└                                             ┘
source

Constraint programming sets

MathOptInterface.AllDifferentType
AllDifferent(dimension::Int)

The set $\{x \in \mathbb{Z}^{d}\}$ such that no two elements in $x$ take the same value and dimension = d.

Also known as

This constraint is called all_different in MiniZinc, and is sometimes also called distinct.

Example

To enforce x[1] != x[2] AND x[1] != x[3] AND x[2] != x[3]:

julia> import MathOptInterface as MOI
+└                                             ┘
source

Constraint programming sets

MathOptInterface.AllDifferentType
AllDifferent(dimension::Int)

The set $\{x \in \mathbb{Z}^{d}\}$ such that no two elements in $x$ take the same value and dimension = d.

Also known as

This constraint is called all_different in MiniZinc, and is sometimes also called distinct.

Example

To enforce x[1] != x[2] AND x[1] != x[3] AND x[2] != x[3]:

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -598,7 +598,7 @@
  MOI.VariableIndex(3)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.AllDifferent(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.AllDifferent}(1)
source
MathOptInterface.BinPackingType
BinPacking(c::T, w::Vector{T}) where {T}

The set $\{x \in \mathbb{Z}^d\}$ where d = length(w), such that each item i in 1:d of weight w[i] is put into bin x[i], and the total weight of each bin does not exceed c.

There are additional assumptions that the capacity, c, and the weights, w, must all be non-negative.

The bin numbers depend on the bounds of x, so they may be something other than the integers 1:d.

Also known as

This constraint is called bin_packing in MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.AllDifferent}(1)
source
MathOptInterface.BinPackingType
BinPacking(c::T, w::Vector{T}) where {T}

The set $\{x \in \mathbb{Z}^d\}$ where d = length(w), such that each item i in 1:d of weight w[i] is put into bin x[i], and the total weight of each bin does not exceed c.

There are additional assumptions that the capacity, c, and the weights, w, must all be non-negative.

The bin numbers depend on the bounds of x, so they may be something other than the integers 1:d.

Also known as

This constraint is called bin_packing in MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -635,7 +635,7 @@
  MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}}(5)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(bins), MOI.BinPacking(3.0, weights))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.BinPacking{Float64}}(1)
source
MathOptInterface.CircuitType
Circuit(dimension::Int)

The set $\{x \in \{1..d\}^d\}$ that constraints $x$ to be a circuit, such that $x_i = j$ means that $j$ is the successor of $i$, and dimension = d.

Graphs with multiple independent circuits, such as [2, 1, 3] and [2, 1, 4, 3], are not valid.

Also known as

This constraint is called circuit in MiniZinc, and it is equivalent to forming a (potentially sub-optimal) tour in the travelling salesperson problem.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.BinPacking{Float64}}(1)
source
MathOptInterface.CircuitType
Circuit(dimension::Int)

The set $\{x \in \{1..d\}^d\}$ that constraints $x$ to be a circuit, such that $x_i = j$ means that $j$ is the successor of $i$, and dimension = d.

Graphs with multiple independent circuits, such as [2, 1, 3] and [2, 1, 4, 3], are not valid.

Also known as

This constraint is called circuit in MiniZinc, and it is equivalent to forming a (potentially sub-optimal) tour in the travelling salesperson problem.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -646,7 +646,7 @@
  MOI.VariableIndex(3)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Circuit(3))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Circuit}(1)
source
MathOptInterface.CountAtLeastType
CountAtLeast(n::Int, d::Vector{Int}, set::Set{Int})

The set $\{x \in \mathbb{Z}^{d_1 + d_2 + \ldots d_N}\}$, where x is partitioned into N subsets ($\{x_1, \ldots, x_{d_1}\}$, $\{x_{d_1 + 1}, \ldots, x_{d_1 + d_2}\}$ and so on), and at least $n$ elements of each subset take one of the values in set.

Also known as

This constraint is called at_least in MiniZinc.

Example

To ensure that 3 appears at least once in each of the subsets {a, b} and {b, c}:

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Circuit}(1)
source
MathOptInterface.CountAtLeastType
CountAtLeast(n::Int, d::Vector{Int}, set::Set{Int})

The set $\{x \in \mathbb{Z}^{d_1 + d_2 + \ldots d_N}\}$, where x is partitioned into N subsets ($\{x_1, \ldots, x_{d_1}\}$, $\{x_{d_1 + 1}, \ldots, x_{d_1 + d_2}\}$ and so on), and at least $n$ elements of each subset take one of the values in set.

Also known as

This constraint is called at_least in MiniZinc.

Example

To ensure that 3 appears at least once in each of the subsets {a, b} and {b, c}:

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -663,7 +663,7 @@
 (MathOptInterface.VariableIndex[MOI.VariableIndex(1), MOI.VariableIndex(2), MOI.VariableIndex(2), MOI.VariableIndex(3)], [2, 2], [3])
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.CountAtLeast(1, d, Set(set)))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountAtLeast}(1)
source
MathOptInterface.CountBelongsType
CountBelongs(dimenson::Int, set::Set{Int})

The set $\{(n, x) \in \mathbb{Z}^{1+d}\}$, such that n elements of the vector x take on of the values in set and dimension = 1 + d.

Also known as

This constraint is called among by MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountAtLeast}(1)
source
MathOptInterface.CountBelongsType
CountBelongs(dimenson::Int, set::Set{Int})

The set $\{(n, x) \in \mathbb{Z}^{1+d}\}$, such that n elements of the vector x take on of the values in set and dimension = 1 + d.

Also known as

This constraint is called among by MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -683,7 +683,7 @@
   3
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([n; x]), MOI.CountBelongs(4, set))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountBelongs}(1)
source
MathOptInterface.CountDistinctType
CountDistinct(dimension::Int)

The set $\{(n, x) \in \mathbb{Z}^{1+d}\}$, such that the number of distinct values in x is n and dimension = 1 + d.

Also known as

This constraint is called nvalues in MiniZinc.

Example

To model:

  • if n == 1, then x[1] == x[2] == x[3]
  • if n == 2, then
    • x[1] == x[2] != x[3] or
    • x[1] != x[2] == x[3] or
    • x[1] == x[3] != x[2]
  • if n == 3, then x[1] != x[2], x[2] != x[3] and x[3] != x[1]
julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountBelongs}(1)
source
MathOptInterface.CountDistinctType
CountDistinct(dimension::Int)

The set $\{(n, x) \in \mathbb{Z}^{1+d}\}$, such that the number of distinct values in x is n and dimension = 1 + d.

Also known as

This constraint is called nvalues in MiniZinc.

Example

To model:

  • if n == 1, then x[1] == x[2] == x[3]
  • if n == 2, then
    • x[1] == x[2] != x[3] or
    • x[1] != x[2] == x[3] or
    • x[1] == x[3] != x[2]
  • if n == 3, then x[1] != x[2], x[2] != x[3] and x[3] != x[1]
julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -697,7 +697,7 @@
  MOI.VariableIndex(4)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(vcat(n, x)), MOI.CountDistinct(4))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountDistinct}(1)

Relationship to AllDifferent

When the first element is d, CountDistinct is equivalent to an AllDifferent constraint.

source
MathOptInterface.CountGreaterThanType
CountGreaterThan(dimension::Int)

The set $\{(c, y, x) \in \mathbb{Z}^{1+1+d}\}$, such that c is strictly greater than the number of occurences of y in x and dimension = 1 + 1 + d.

Also known as

This constraint is called count_gt in MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountDistinct}(1)

Relationship to AllDifferent

When the first element is d, CountDistinct is equivalent to an AllDifferent constraint.

source
MathOptInterface.CountGreaterThanType
CountGreaterThan(dimension::Int)

The set $\{(c, y, x) \in \mathbb{Z}^{1+1+d}\}$, such that c is strictly greater than the number of occurences of y in x and dimension = 1 + 1 + d.

Also known as

This constraint is called count_gt in MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -714,7 +714,7 @@
  MOI.VariableIndex(5)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([c; y; x]), MOI.CountGreaterThan(5))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountGreaterThan}(1)
source
MathOptInterface.CumulativeType
Cumulative(dimension::Int)

The set $\{(s, d, r, b) \in \mathbb{Z}^{3n+1}\}$, representing the cumulative global constraint, where n == length(s) == length(r) == length(b) and dimension = 3n + 1.

Cumulative requires that a set of tasks given by start times $s$, durations $d$, and resource requirements $r$, never requires more than the global resource bound $b$ at any one time.

Also known as

This constraint is called cumulative in MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.CountGreaterThan}(1)
source
MathOptInterface.CumulativeType
Cumulative(dimension::Int)

The set $\{(s, d, r, b) \in \mathbb{Z}^{3n+1}\}$, representing the cumulative global constraint, where n == length(s) == length(r) == length(b) and dimension = 3n + 1.

Cumulative requires that a set of tasks given by start times $s$, durations $d$, and resource requirements $r$, never requires more than the global resource bound $b$ at any one time.

Also known as

This constraint is called cumulative in MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -740,7 +740,7 @@
 (MOI.VariableIndex(10), MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.Integer}(10))
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([s; d; r; b]), MOI.Cumulative(10))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Cumulative}(1)
source
MathOptInterface.PathType
Path(from::Vector{Int}, to::Vector{Int})

Given a graph comprised of a set of nodes 1..N and a set of arcs 1..E represented by an edge from node from[i] to node to[i], Path constrains the set $(s, t, ns, es) \in (1..N)\times(1..E)\times\{0,1\}^N\times\{0,1\}^E$, to form subgraph that is a path from node s to node t, where node n is in the path if ns[n] is 1, and edge e is in the path if es[e] is 1.

The path must be acyclic, and it must traverse all nodes n for which ns[n] is 1, and all edges e for which es[e] is 1.

Also known as

This constraint is called path in MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Cumulative}(1)
source
MathOptInterface.PathType
Path(from::Vector{Int}, to::Vector{Int})

Given a graph comprised of a set of nodes 1..N and a set of arcs 1..E represented by an edge from node from[i] to node to[i], Path constrains the set $(s, t, ns, es) \in (1..N)\times(1..E)\times\{0,1\}^N\times\{0,1\}^E$, to form subgraph that is a path from node s to node t, where node n is in the path if ns[n] is 1, and edge e is in the path if es[e] is 1.

The path must be acyclic, and it must traverse all nodes n for which ns[n] is 1, and all edges e for which es[e] is 1.

Also known as

This constraint is called path in MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -800,7 +800,7 @@
  MathOptInterface.ConstraintIndex{MathOptInterface.VariableIndex, MathOptInterface.ZeroOne}(11)
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables([s; t; ns; es]), MOI.Path(from, to))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Path}(1)
source
MathOptInterface.ReifiedType
Reified(set::AbstractSet)

The constraint $[z; f(x)] \in Reified(S)$ ensures that $f(x) \in S$ if and only if $z == 1$, where $z \in \{0, 1\}$.

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Path}(1)
source
MathOptInterface.ReifiedType
Reified(set::AbstractSet)

The constraint $[z; f(x)] \in Reified(S)$ ensures that $f(x) \in S$ if and only if $z == 1$, where $z \in \{0, 1\}$.

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}());
 
@@ -815,7 +815,7 @@
            MOI.Utilities.vectorize([z, 2.0 * x]),
            MOI.Reified(MOI.GreaterThan(1.0)),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Reified{MathOptInterface.GreaterThan{Float64}}}(1)
source
MathOptInterface.TableType
Table(table::Matrix{T}) where {T}

The set $\{x \in \mathbb{R}^d\}$ where d = size(table, 2), such that x belongs to one row of table. That is, there exists some j in 1:size(table, 1), such that x[i] = table[j, i] for all i=1:size(table, 2).

Also known as

This constraint is called table in MiniZinc.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Reified{MathOptInterface.GreaterThan{Float64}}}(1)
source
MathOptInterface.TableType
Table(table::Matrix{T}) where {T}

The set $\{x \in \mathbb{R}^d\}$ where d = size(table, 2), such that x belongs to one row of table. That is, there exists some j in 1:size(table, 1), such that x[i] = table[j, i] for all i=1:size(table, 2).

Also known as

This constraint is called table in MiniZinc.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -833,7 +833,7 @@
  1.0  1.0  1.0
 
 julia> MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.Table(table))
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Table{Float64}}(1)
source

Matrix sets

Matrix sets are vectorized to be subtypes of AbstractVectorSet.

For sets of symmetric matrices, storing both the (i, j) and (j, i) elements is redundant. Use the AbstractSymmetricMatrixSetTriangle set to represent only the vectorization of the upper triangular part of the matrix.

When the matrix of expressions constrained to be in the set is not symmetric, and hence additional constraints are needed to force the equality of the (i, j) and (j, i) elements, use the AbstractSymmetricMatrixSetSquare set.

The Bridges.Constraint.SquareBridge can transform a set from the square form to the triangular_form by adding appropriate constraints if the (i, j) and (j, i) expressions are different.

MathOptInterface.AbstractSymmetricMatrixSetTriangleType
abstract type AbstractSymmetricMatrixSetTriangle <: AbstractVectorSet end

Abstract supertype for subsets of the (vectorized) cone of symmetric matrices, with side_dimension rows and columns. The entries of the upper-right triangular part of the matrix are given column by column (or equivalently, the entries of the lower-left triangular part are given row by row). A vectorized cone of dimension $n$ corresponds to a square matrix with side dimension $\sqrt{1/4 + 2 n} - 1/2$. (Because a $d \times d$ matrix has $d(d + 1) / 2$ elements in the upper or lower triangle.)

Example

The matrix

\[\begin{bmatrix} +MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.Table{Float64}}(1)

source

Matrix sets

Matrix sets are vectorized to be subtypes of AbstractVectorSet.

For sets of symmetric matrices, storing both the (i, j) and (j, i) elements is redundant. Use the AbstractSymmetricMatrixSetTriangle set to represent only the vectorization of the upper triangular part of the matrix.

When the matrix of expressions constrained to be in the set is not symmetric, and hence additional constraints are needed to force the equality of the (i, j) and (j, i) elements, use the AbstractSymmetricMatrixSetSquare set.

The Bridges.Constraint.SquareBridge can transform a set from the square form to the triangular_form by adding appropriate constraints if the (i, j) and (j, i) expressions are different.

MathOptInterface.AbstractSymmetricMatrixSetTriangleType
abstract type AbstractSymmetricMatrixSetTriangle <: AbstractVectorSet end

Abstract supertype for subsets of the (vectorized) cone of symmetric matrices, with side_dimension rows and columns. The entries of the upper-right triangular part of the matrix are given column by column (or equivalently, the entries of the lower-left triangular part are given row by row). A vectorized cone of dimension $n$ corresponds to a square matrix with side dimension $\sqrt{1/4 + 2 n} - 1/2$. (Because a $d \times d$ matrix has $d(d + 1) / 2$ elements in the upper or lower triangle.)

Example

The matrix

\[\begin{bmatrix} 1 & 2 & 4\\ 2 & 3 & 5\\ 4 & 5 & 6 @@ -861,24 +861,24 @@ y_1 & y_2\\ y_2 & y_3 \end{pmatrix} -= 2y_2.\]

References

[1] Boyd, S. and Vandenberghe, L.. Convex optimization. Cambridge university press, 2004.

source
MathOptInterface.AbstractSymmetricMatrixSetSquareType
abstract type AbstractSymmetricMatrixSetSquare <: AbstractVectorSet end

Abstract supertype for subsets of the (vectorized) cone of symmetric matrices, with side_dimension rows and columns. The entries of the matrix are given column by column (or equivalently, row by row). The matrix is both constrained to be symmetric and to have its triangular_form belong to the corresponding set. That is, if the functions in entries $(i, j)$ and $(j, i)$ are different, then a constraint will be added to make sure that the entries are equal.

Example

PositiveSemidefiniteConeSquare is a subtype of AbstractSymmetricMatrixSetSquare and constraining the matrix

\[\begin{bmatrix} += 2y_2.\]

References

[1] Boyd, S. and Vandenberghe, L.. Convex optimization. Cambridge university press, 2004.

source
MathOptInterface.AbstractSymmetricMatrixSetSquareType
abstract type AbstractSymmetricMatrixSetSquare <: AbstractVectorSet end

Abstract supertype for subsets of the (vectorized) cone of symmetric matrices, with side_dimension rows and columns. The entries of the matrix are given column by column (or equivalently, row by row). The matrix is both constrained to be symmetric and to have its triangular_form belong to the corresponding set. That is, if the functions in entries $(i, j)$ and $(j, i)$ are different, then a constraint will be added to make sure that the entries are equal.

Example

PositiveSemidefiniteConeSquare is a subtype of AbstractSymmetricMatrixSetSquare and constraining the matrix

\[\begin{bmatrix} 1 & -y\\ -z & 0\\ -\end{bmatrix}\]

to be symmetric positive semidefinite can be achieved by constraining the vector $(1, -z, -y, 0)$ (or $(1, -y, -z, 0)$) to belong to the PositiveSemidefiniteConeSquare(2). It both constrains $y = z$ and $(1, -y, 0)$ (or $(1, -z, 0)$) to be in PositiveSemidefiniteConeTriangle(2), since triangular_form(PositiveSemidefiniteConeSquare) is PositiveSemidefiniteConeTriangle.

source
MathOptInterface.side_dimensionFunction
side_dimension(
+\end{bmatrix}\]

to be symmetric positive semidefinite can be achieved by constraining the vector $(1, -z, -y, 0)$ (or $(1, -y, -z, 0)$) to belong to the PositiveSemidefiniteConeSquare(2). It both constrains $y = z$ and $(1, -y, 0)$ (or $(1, -z, 0)$) to be in PositiveSemidefiniteConeTriangle(2), since triangular_form(PositiveSemidefiniteConeSquare) is PositiveSemidefiniteConeTriangle.

source

List of recognized matrix sets.

MathOptInterface.PositiveSemidefiniteConeSquareType
PositiveSemidefiniteConeSquare(side_dimension::Int) <: AbstractSymmetricMatrixSetSquare

The cone of symmetric positive semidefinite matrices, with non-negative side length side_dimension.

See AbstractSymmetricMatrixSetSquare for more details on the vectorized form.

The entries of the matrix are given column by column (or equivalently, row by row).

The matrix is both constrained to be symmetric and to be positive semidefinite. That is, if the functions in entries $(i, j)$ and $(j, i)$ are different, then a constraint will be added to make sure that the entries are equal.

Example

Constraining the matrix

\[\begin{bmatrix} +)

Side dimension of the matrices in set.

Convention

By convention, the side dimension should be stored in the side_dimension field. If this is not the case for a subtype of AbstractSymmetricMatrixSetTriangle, or AbstractSymmetricMatrixSetSquare you must implement this method.

source

List of recognized matrix sets.

MathOptInterface.PositiveSemidefiniteConeSquareType
PositiveSemidefiniteConeSquare(side_dimension::Int) <: AbstractSymmetricMatrixSetSquare

The cone of symmetric positive semidefinite matrices, with non-negative side length side_dimension.

See AbstractSymmetricMatrixSetSquare for more details on the vectorized form.

The entries of the matrix are given column by column (or equivalently, row by row).

The matrix is both constrained to be symmetric and to be positive semidefinite. That is, if the functions in entries $(i, j)$ and $(j, i)$ are different, then a constraint will be added to make sure that the entries are equal.

Example

Constraining the matrix

\[\begin{bmatrix} 1 & -y\\ -z & 0\\ -\end{bmatrix}\]

to be symmetric positive semidefinite can be achieved by constraining the vector $(1, -z, -y, 0)$ (or $(1, -y, -z, 0)$) to belong to the PositiveSemidefiniteConeSquare(2).

It both constrains $y = z$ and $(1, -y, 0)$ (or $(1, -z, 0)$) to be in PositiveSemidefiniteConeTriangle(2).

source
MathOptInterface.HermitianPositiveSemidefiniteConeTriangleType
HermitianPositiveSemidefiniteConeTriangle(side_dimension::Int) <: AbstractVectorSet

The (vectorized) cone of Hermitian positive semidefinite matrices, with non-negative side_dimension rows and columns.

Becaue the matrix is Hermitian, the diagonal elements are real, and the complex-valued lower triangular entries are obtained as the conjugate of corresponding upper triangular entries.

Vectorization format

The vectorized form starts with real part of the entries of the upper triangular part of the matrix, given column by column as explained in AbstractSymmetricMatrixSetSquare.

It is then followed by the imaginary part of the off-diagonal entries of the upper triangular part, also given column by column.

For example, the matrix

\[\begin{bmatrix} +\end{bmatrix}\]

to be symmetric positive semidefinite can be achieved by constraining the vector $(1, -z, -y, 0)$ (or $(1, -y, -z, 0)$) to belong to the PositiveSemidefiniteConeSquare(2).

It both constrains $y = z$ and $(1, -y, 0)$ (or $(1, -z, 0)$) to be in PositiveSemidefiniteConeTriangle(2).

source
MathOptInterface.HermitianPositiveSemidefiniteConeTriangleType
HermitianPositiveSemidefiniteConeTriangle(side_dimension::Int) <: AbstractVectorSet

The (vectorized) cone of Hermitian positive semidefinite matrices, with non-negative side_dimension rows and columns.

Becaue the matrix is Hermitian, the diagonal elements are real, and the complex-valued lower triangular entries are obtained as the conjugate of corresponding upper triangular entries.

Vectorization format

The vectorized form starts with real part of the entries of the upper triangular part of the matrix, given column by column as explained in AbstractSymmetricMatrixSetSquare.

It is then followed by the imaginary part of the off-diagonal entries of the upper triangular part, also given column by column.

For example, the matrix

\[\begin{bmatrix} 1 & 2 + 7im & 4 + 8im\\ 2 - 7im & 3 & 5 + 9im\\ 4 - 8im & 5 - 9im & 6 -\end{bmatrix}\]

has side_dimension 3 and is represented as the vector $[1, 2, 3, 4, 5, 6, 7, 8, 9]$.

source
MathOptInterface.LogDetConeTriangleType
LogDetConeTriangle(side_dimension::Int)

The log-determinant cone $\{ (t, u, X) \in \mathbb{R}^{2 + d(d+1)/2} : t \le u \log(\det(X/u)), u > 0 \}$, where the matrix X is represented in the same symmetric packed format as in the PositiveSemidefiniteConeTriangle.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
+\end{bmatrix}\]

has side_dimension 3 and is represented as the vector $[1, 2, 3, 4, 5, 6, 7, 8, 9]$.

source
MathOptInterface.LogDetConeTriangleType
LogDetConeTriangle(side_dimension::Int)

The log-determinant cone $\{ (t, u, X) \in \mathbb{R}^{2 + d(d+1)/2} : t \le u \log(\det(X/u)), u > 0 \}$, where the matrix X is represented in the same symmetric packed format as in the PositiveSemidefiniteConeTriangle.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -892,7 +892,7 @@
            MOI.VectorOfVariables([t; X]),
            MOI.LogDetConeTriangle(2),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.LogDetConeTriangle}(1)
source
MathOptInterface.LogDetConeSquareType
LogDetConeSquare(side_dimension::Int)

The log-determinant cone $\{ (t, u, X) \in \mathbb{R}^{2 + d^2} : t \le u \log(\det(X/u)), X \text{ symmetric}, u > 0 \}$, where the matrix X is represented in the same format as in the PositiveSemidefiniteConeSquare.

Similarly to PositiveSemidefiniteConeSquare, constraints are added to ensure that X is symmetric.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.LogDetConeTriangle}(1)
source
MathOptInterface.LogDetConeSquareType
LogDetConeSquare(side_dimension::Int)

The log-determinant cone $\{ (t, u, X) \in \mathbb{R}^{2 + d^2} : t \le u \log(\det(X/u)), X \text{ symmetric}, u > 0 \}$, where the matrix X is represented in the same format as in the PositiveSemidefiniteConeSquare.

Similarly to PositiveSemidefiniteConeSquare, constraints are added to ensure that X is symmetric.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -909,7 +909,7 @@
            MOI.VectorOfVariables([t; vec(X)]),
            MOI.LogDetConeSquare(2),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.LogDetConeSquare}(1)
source
MathOptInterface.RootDetConeTriangleType
RootDetConeTriangle(side_dimension::Int)

The root-determinant cone $\{ (t, X) \in \mathbb{R}^{1 + d(d+1)/2} : t \le \det(X)^{1/d} \}$, where the matrix X is represented in the same symmetric packed format as in the PositiveSemidefiniteConeTriangle.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.LogDetConeSquare}(1)
source
MathOptInterface.RootDetConeTriangleType
RootDetConeTriangle(side_dimension::Int)

The root-determinant cone $\{ (t, X) \in \mathbb{R}^{1 + d(d+1)/2} : t \le \det(X)^{1/d} \}$, where the matrix X is represented in the same symmetric packed format as in the PositiveSemidefiniteConeTriangle.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -923,7 +923,7 @@
            MOI.VectorOfVariables([t; X]),
            MOI.RootDetConeTriangle(2),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RootDetConeTriangle}(1)
source
MathOptInterface.RootDetConeSquareType
RootDetConeSquare(side_dimension::Int)

The root-determinant cone $\{ (t, X) \in \mathbb{R}^{1 + d^2} : t \le \det(X)^{1/d}, X \text{ symmetric} \}$, where the matrix X is represented in the same format as PositiveSemidefiniteConeSquare.

Similarly to PositiveSemidefiniteConeSquare, constraints are added to ensure that X is symmetric.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RootDetConeTriangle}(1)
source
MathOptInterface.RootDetConeSquareType
RootDetConeSquare(side_dimension::Int)

The root-determinant cone $\{ (t, X) \in \mathbb{R}^{1 + d^2} : t \le \det(X)^{1/d}, X \text{ symmetric} \}$, where the matrix X is represented in the same format as PositiveSemidefiniteConeSquare.

Similarly to PositiveSemidefiniteConeSquare, constraints are added to ensure that X is symmetric.

The non-negative argument side_dimension is the side dimension of the matrix X, that is, its number of rows or columns.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
@@ -940,4 +940,4 @@
            MOI.VectorOfVariables([t; vec(X)]),
            MOI.RootDetConeSquare(2),
        )
-MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RootDetConeSquare}(1)
source
+MathOptInterface.ConstraintIndex{MathOptInterface.VectorOfVariables, MathOptInterface.RootDetConeSquare}(1)source diff --git a/dev/reference/variables/index.html b/dev/reference/variables/index.html index 3b75e4d8a4..4b9135c030 100644 --- a/dev/reference/variables/index.html +++ b/dev/reference/variables/index.html @@ -4,18 +4,18 @@ julia> model = MOI.Utilities.Model{Float64}(); julia> x = MOI.add_variable(model) -MOI.VariableIndex(1)source
MathOptInterface.add_variablesFunction
add_variables(model::ModelLike, n::Int)::Vector{VariableIndex}

Add n scalar variables to the model, returning a vector of variable indices.

An AddVariableNotAllowed error is thrown if adding variables cannot be done in the current state of the model model.

Example

julia> import MathOptInterface as MOI
+MOI.VariableIndex(1)
source
MathOptInterface.add_variablesFunction
add_variables(model::ModelLike, n::Int)::Vector{VariableIndex}

Add n scalar variables to the model, returning a vector of variable indices.

An AddVariableNotAllowed error is thrown if adding variables cannot be done in the current state of the model model.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Utilities.Model{Float64}();
 
 julia> MOI.add_variables(model, 2)
 2-element Vector{MathOptInterface.VariableIndex}:
  MOI.VariableIndex(1)
- MOI.VariableIndex(2)
source
MathOptInterface.add_constrained_variableFunction
add_constrained_variable(
     model::ModelLike,
     set::AbstractScalarSet
 )::Tuple{MOI.VariableIndex,
-         MOI.ConstraintIndex{MOI.VariableIndex, typeof(set)}}

Add to model a scalar variable constrained to belong to set, returning the index of the variable created and the index of the constraint constraining the variable to belong to set.

By default, this function falls back to creating a free variable with add_variable and then constraining it to belong to set with add_constraint.

source
add_constrained_variable(
+         MOI.ConstraintIndex{MOI.VariableIndex, typeof(set)}}

Add to model a scalar variable constrained to belong to set, returning the index of the variable created and the index of the constraint constraining the variable to belong to set.

By default, this function falls back to creating a free variable with add_variable and then constraining it to belong to set with add_constraint.

source
add_constrained_variable(
     model::ModelLike,
     set::Tuple{<:GreaterThan,<:LessThan},
 )

A special-case method to add a scalar variable with a lower and upper bound.

This method should be implemented by optimizers which have native support for adding a variable with bounds and which cannot performantly modify the variable bounds after creation.

Example

julia> import MathOptInterface as MOI
@@ -41,22 +41,22 @@
  v[1] >= 1.0
 
 VariableIndex-in-LessThan{Float64}
- v[1] <= 2.0
source
MathOptInterface.add_constrained_variablesFunction
add_constrained_variables(
     model::ModelLike,
     sets::AbstractVector{<:AbstractScalarSet}
 )::Tuple{
     Vector{MOI.VariableIndex},
     Vector{MOI.ConstraintIndex{MOI.VariableIndex,eltype(sets)}},
-}

Add to model scalar variables constrained to belong to sets, returning the indices of the variables created and the indices of the constraints constraining the variables to belong to each set in sets. That is, if it returns variables and constraints, constraints[i] is the index of the constraint constraining variable[i] to belong to sets[i].

By default, this function falls back to calling add_constrained_variable on each set.

source
add_constrained_variables(
+}

Add to model scalar variables constrained to belong to sets, returning the indices of the variables created and the indices of the constraints constraining the variables to belong to each set in sets. That is, if it returns variables and constraints, constraints[i] is the index of the constraint constraining variable[i] to belong to sets[i].

By default, this function falls back to calling add_constrained_variable on each set.

source
add_constrained_variables(
     model::ModelLike,
     set::AbstractVectorSet,
 )::Tuple{
     Vector{MOI.VariableIndex},
     MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)},
-}

Add to model a vector of variables constrained to belong to set, returning the indices of the variables created and the index of the constraint constraining the vector of variables to belong to set.

By default, this function falls back to creating free variables with add_variables and then constraining it to belong to set with add_constraint.

source
MathOptInterface.supports_add_constrained_variableFunction
supports_add_constrained_variable(
+}

Add to model a vector of variables constrained to belong to set, returning the indices of the variables created and the index of the constraint constraining the vector of variables to belong to set.

By default, this function falls back to creating free variables with add_variables and then constraining it to belong to set with add_constraint.

source
MathOptInterface.supports_add_constrained_variableFunction
supports_add_constrained_variable(
     model::ModelLike,
     S::Type{<:AbstractScalarSet}
-)::Bool

Return a Bool indicating whether model supports constraining a variable to belong to a set of type S either on creation of the variable with add_constrained_variable or after the variable is created with add_constraint.

By default, this function falls back to supports_add_constrained_variables(model, Reals) && supports_constraint(model, MOI.VariableIndex, S) which is the correct definition for most models.

Example

Suppose that a solver supports only two kind of variables: binary variables and continuous variables with a lower bound. If the solver decides not to support VariableIndex-in-Binary and VariableIndex-in-GreaterThan constraints, it only has to implement add_constrained_variable for these two sets which prevents the user to add both a binary constraint and a lower bound on the same variable. Moreover, if the user adds a VariableIndex-in-GreaterThan constraint, implementing this interface (that is, supports_add_constrained_variables) enables the constraint to be transparently bridged into a supported constraint.

source
MathOptInterface.supports_add_constrained_variablesFunction
supports_add_constrained_variables(
+)::Bool

Return a Bool indicating whether model supports constraining a variable to belong to a set of type S either on creation of the variable with add_constrained_variable or after the variable is created with add_constraint.

By default, this function falls back to supports_add_constrained_variables(model, Reals) && supports_constraint(model, MOI.VariableIndex, S) which is the correct definition for most models.

Example

Suppose that a solver supports only two kind of variables: binary variables and continuous variables with a lower bound. If the solver decides not to support VariableIndex-in-Binary and VariableIndex-in-GreaterThan constraints, it only has to implement add_constrained_variable for these two sets which prevents the user to add both a binary constraint and a lower bound on the same variable. Moreover, if the user adds a VariableIndex-in-GreaterThan constraint, implementing this interface (that is, supports_add_constrained_variables) enables the constraint to be transparently bridged into a supported constraint.

source
MathOptInterface.supports_add_constrained_variablesFunction
supports_add_constrained_variables(
     model::ModelLike,
     S::Type{<:AbstractVectorSet}
-)::Bool

Return a Bool indicating whether model supports constraining a vector of variables to belong to a set of type S either on creation of the vector of variables with add_constrained_variables or after the variable is created with add_constraint.

By default, if S is Reals then this function returns true and otherwise, it falls back to supports_add_constrained_variables(model, Reals) && supports_constraint(model, MOI.VectorOfVariables, S) which is the correct definition for most models.

Example

In the standard conic form (see Duality), the variables are grouped into several cones and the constraints are affine equality constraints. If Reals is not one of the cones supported by the solvers then it needs to implement supports_add_constrained_variables(::Optimizer, ::Type{Reals}) = false as free variables are not supported. The solvers should then implement supports_add_constrained_variables(::Optimizer, ::Type{<:SupportedCones}) = true where SupportedCones is the union of all cone types that are supported; it does not have to implement the method supports_constraint(::Type{VectorOfVariables}, Type{<:SupportedCones}) as it should return false and it's the default. This prevents the user to constrain the same variable in two different cones. When a VectorOfVariables-in-S is added, the variables of the vector have already been created so they already belong to given cones. If bridges are enabled, the constraint will therefore be bridged by adding slack variables in S and equality constraints ensuring that the slack variables are equal to the corresponding variables of the given constraint function.

Note that there may also be sets for which !supports_add_constrained_variables(model, S) and supports_constraint(model, MOI.VectorOfVariables, S). For instance, suppose a solver supports positive semidefinite variable constraints and two types of variables: binary variables and nonnegative variables. Then the solver should support adding VectorOfVariables-in-PositiveSemidefiniteConeTriangle constraints, but it should not support creating variables constrained to belong to the PositiveSemidefiniteConeTriangle because the variables in PositiveSemidefiniteConeTriangle should first be created as either binary or non-negative.

source
MathOptInterface.is_validMethod
is_valid(model::ModelLike, index::Index)::Bool

Return a Bool indicating whether this index refers to a valid object in the model model.

source
MathOptInterface.deleteMethod
delete(model::ModelLike, index::Index)

Delete the referenced object from the model. Throw DeleteNotAllowed if if index cannot be deleted.

The following modifications also take effect if Index is VariableIndex:

  • If index used in the objective function, it is removed from the function, that is, it is substituted for zero.
  • For each func-in-set constraint of the model:
    • If func isa VariableIndex and func == index then the constraint is deleted.
    • If func isa VectorOfVariables and index in func.variables then
      • if length(func.variables) == 1 is one, the constraint is deleted;
      • if length(func.variables) > 1 and supports_dimension_update(set) then then the variable is removed from func and set is replaced by update_dimension(set, MOI.dimension(set) - 1).
      • Otherwise, a DeleteNotAllowed error is thrown.
    • Otherwise, the variable is removed from func, that is, it is substituted for zero.
source
MathOptInterface.deleteMethod
delete(model::ModelLike, indices::Vector{R<:Index}) where {R}

Delete the referenced objects in the vector indices from the model. It may be assumed that R is a concrete type. The default fallback sequentially deletes the individual items in indices, although specialized implementations may be more efficient.

source

Attributes

MathOptInterface.VariableNameType
VariableName()

A variable attribute for a string identifying the variable. It is valid for two variables to have the same name; however, variables with duplicate names cannot be looked up using get. It has a default value of "" if not set`.

source
MathOptInterface.VariablePrimalStartType
VariablePrimalStart()

A variable attribute for the initial assignment to some primal variable's value that the optimizer may use to warm-start the solve. May be a number or nothing (unset).

source
MathOptInterface.VariablePrimalType
VariablePrimal(result_index::Int = 1)

A variable attribute for the assignment to some primal variable's value in result result_index. If result_index is omitted, it is 1 by default.

If the solver does not have a primal value for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the VariablePrimal attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.VariableBasisStatusType
VariableBasisStatus(result_index::Int = 1)

A variable attribute for the BasisStatusCode of a variable in result result_index, with respect to an available optimal solution basis.

If the solver does not have a basis status for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the VariableBasisStatus attribute.

See ResultCount for information on how the results are ordered.

source
+)::Bool

Return a Bool indicating whether model supports constraining a vector of variables to belong to a set of type S either on creation of the vector of variables with add_constrained_variables or after the variable is created with add_constraint.

By default, if S is Reals then this function returns true and otherwise, it falls back to supports_add_constrained_variables(model, Reals) && supports_constraint(model, MOI.VectorOfVariables, S) which is the correct definition for most models.

Example

In the standard conic form (see Duality), the variables are grouped into several cones and the constraints are affine equality constraints. If Reals is not one of the cones supported by the solvers then it needs to implement supports_add_constrained_variables(::Optimizer, ::Type{Reals}) = false as free variables are not supported. The solvers should then implement supports_add_constrained_variables(::Optimizer, ::Type{<:SupportedCones}) = true where SupportedCones is the union of all cone types that are supported; it does not have to implement the method supports_constraint(::Type{VectorOfVariables}, Type{<:SupportedCones}) as it should return false and it's the default. This prevents the user to constrain the same variable in two different cones. When a VectorOfVariables-in-S is added, the variables of the vector have already been created so they already belong to given cones. If bridges are enabled, the constraint will therefore be bridged by adding slack variables in S and equality constraints ensuring that the slack variables are equal to the corresponding variables of the given constraint function.

Note that there may also be sets for which !supports_add_constrained_variables(model, S) and supports_constraint(model, MOI.VectorOfVariables, S). For instance, suppose a solver supports positive semidefinite variable constraints and two types of variables: binary variables and nonnegative variables. Then the solver should support adding VectorOfVariables-in-PositiveSemidefiniteConeTriangle constraints, but it should not support creating variables constrained to belong to the PositiveSemidefiniteConeTriangle because the variables in PositiveSemidefiniteConeTriangle should first be created as either binary or non-negative.

source
MathOptInterface.is_validMethod
is_valid(model::ModelLike, index::Index)::Bool

Return a Bool indicating whether this index refers to a valid object in the model model.

source
MathOptInterface.deleteMethod
delete(model::ModelLike, index::Index)

Delete the referenced object from the model. Throw DeleteNotAllowed if if index cannot be deleted.

The following modifications also take effect if Index is VariableIndex:

  • If index used in the objective function, it is removed from the function, that is, it is substituted for zero.
  • For each func-in-set constraint of the model:
    • If func isa VariableIndex and func == index then the constraint is deleted.
    • If func isa VectorOfVariables and index in func.variables then
      • if length(func.variables) == 1 is one, the constraint is deleted;
      • if length(func.variables) > 1 and supports_dimension_update(set) then then the variable is removed from func and set is replaced by update_dimension(set, MOI.dimension(set) - 1).
      • Otherwise, a DeleteNotAllowed error is thrown.
    • Otherwise, the variable is removed from func, that is, it is substituted for zero.
source
MathOptInterface.deleteMethod
delete(model::ModelLike, indices::Vector{R<:Index}) where {R}

Delete the referenced objects in the vector indices from the model. It may be assumed that R is a concrete type. The default fallback sequentially deletes the individual items in indices, although specialized implementations may be more efficient.

source

Attributes

MathOptInterface.VariableNameType
VariableName()

A variable attribute for a string identifying the variable. It is valid for two variables to have the same name; however, variables with duplicate names cannot be looked up using get. It has a default value of "" if not set`.

source
MathOptInterface.VariablePrimalStartType
VariablePrimalStart()

A variable attribute for the initial assignment to some primal variable's value that the optimizer may use to warm-start the solve. May be a number or nothing (unset).

source
MathOptInterface.VariablePrimalType
VariablePrimal(result_index::Int = 1)

A variable attribute for the assignment to some primal variable's value in result result_index. If result_index is omitted, it is 1 by default.

If the solver does not have a primal value for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the VariablePrimal attribute.

See ResultCount for information on how the results are ordered.

source
MathOptInterface.VariableBasisStatusType
VariableBasisStatus(result_index::Int = 1)

A variable attribute for the BasisStatusCode of a variable in result result_index, with respect to an available optimal solution basis.

If the solver does not have a basis status for the variable because the result_index is beyond the available solutions (whose number is indicated by the ResultCount attribute), getting this attribute must throw a ResultIndexBoundsError. Otherwise, if the result is unavailable for another reason (for instance, only a dual solution is available), the result is undefined. Users should first check PrimalStatus before accessing the VariableBasisStatus attribute.

See ResultCount for information on how the results are ordered.

source
diff --git a/dev/release_notes/index.html b/dev/release_notes/index.html index 1301ddb90f..0a884175e2 100644 --- a/dev/release_notes/index.html +++ b/dev/release_notes/index.html @@ -27,4 +27,4 @@ end write(path, s) end -end

v0.9.22 (May 22, 2021)

This release contains backports from the ongoing development of the v0.10 release.

  • Improved type inference in Utilities, Bridges and FileFormats submodules to reduce latency.
  • Improved performance of Utilities.is_canonical.
  • Fixed Utilities.pass_nonvariable_constraints with bridged variables.
  • Fixed performance regression of Utilities.Model.
  • Fixed ordering of objective setting in parser.

v0.9.21 (April 23, 2021)

  • Added supports_shift_constant.
  • Improve performance of bridging quadratic constraints.
  • Add precompilation statements.
  • Large improvements to the documentation.
  • Fix a variety of inference issues, benefiting precompilation and reducing initial latency.
  • RawParameters are now ignored when resetting a CachingOptimizer. Previously, changing the underlying optimizer after RawParameters were set would throw an error.
  • Utilities.AbstractModel is being refactored. This may break users interacting with private fields of a model generated using @model.

v0.9.20 (February 20, 2021)

  • Improved performance of Utilities.ScalarFunctionIterator
  • Added support for compute_conflict to MOI layers
  • Added test with zero off-diagonal quadratic term in objective
  • Fixed double deletion of nested bridged SingleVariable/VectorOfVariables constraints
  • Fixed modification of un-set objective
  • Fixed function modification with duplicate terms
  • Made unit tests abort without failing if the problem class is not supported
  • Formatted code with JuliaFormatter
  • Clarified BasisStatusCode's docstring

v0.9.19 (December 1, 2020)

  • Added CallbackNodeStatus attribute
  • Added bridge from GreaterThan or LessThan to Interval
  • Added tests for infeasibility certificates and double optimize
  • Fixed support for Julia v1.6
  • Re-organized MOI docs and added documentation for adding a test

v0.9.18 (November 3, 2020)

  • Various improvements for working with complex numbers
  • Added GeoMeantoRelEntrBridge to bridge a GeometricMeanCone constraint to a relative entropy constraint

v0.9.17 (September 21, 2020)

  • Fixed CleverDict with variable of negative index value
  • Implement supports_add_constrained_variable for MockOptimizer

v0.9.16 (September 17, 2020)

  • Various fixes:
    • 32-bit support
    • CleverDict with abstract value type
    • Checks in test suite

v0.9.15 (September 14, 2020)

  • Bridges improvements:
    • (R)SOCtoNonConvexQuad bridge
    • ZeroOne bridge
    • Use supports_add_constrained_variable in LazyBridgeOptimizer
    • Exposed VariableBridgeCost and ConstraintBridgeCost attributes
    • Prioritize constraining variables on creation according to these costs
    • Refactor bridge debugging
  • Large performance improvements across all submodules
  • Lots of documentation improvements
  • FileFormats improvements:
    • Update MathOptFormat to v0.5
    • Fix supported objectives in FileFormats
  • Testing improvements:
    • Add name option for basic_constraint_test
  • Bug fixes and missing methods
    • Add length for iterators
    • Fix bug with duplicate terms
    • Fix order of LinearOfConstraintIndices

v0.9.14 (May 30, 2020)

  • Add a solver-independent interface for accessing the set of conflicting constraints an Irreducible Inconsistent Subsystem (#1056).
  • Bump JSONSchema dependency from v0.2 to v0.3 (#1090).
  • Documentation improvements:
    • Fix typos (#1054, #1060, #1061, #1064, #1069, #1070).
    • Remove the outdated recommendation for a package implementing MOI for a solver XXX to be called MathOptInterfaceXXX (#1087).
  • Utilities improvements:
    • Fix is_canonical for quadratic functions (#1081, #1089).
    • Implement add_constrained_variable[s] for CachingOptimizer so that it is added as constrained variables to the underlying optimizer (#1084).
    • Add support for custom objective functions for UniversalFallback (#1086).
    • Deterministic ordering of constraints in UniversalFallback (#1088).
  • Testing improvements:
    • Add NormOneCone/NormInfinityCone tests (#1045).
  • Bridges improvements:
    • Add bridges from Semiinteger and Semicontinuous (#1059).
    • Implement getting ConstraintSet for Variable.FlipSignBridge (#1066).
    • Fix setting ConstraintFunction for Constraint.ScalarizeBridge (#1093).
    • Fix NormOne/NormInf bridges with nonzero constants (#1045).
    • Fix StackOverflow in debug (#1063).
  • FileFormats improvements:
    • [SDPA] Implement the extension for integer variables (#1079).
    • [SDPA] Ignore comments after m and nblocks and detect dat-s extension (#1077).
    • [SDPA] No scaling of off-diagonal coefficient (#1076).
    • [SDPA] Add missing negation of constant (#1075).

v0.9.13 (March 24, 2020)

  • Added tests for Semicontinuous and Semiinteger variables (#1033).
  • Added tests for using ExprGraphs from NLP evaluators (#1043).
  • Update version compatibilities of dependencies (#1034, #1051, #1052).
  • Fixed typos in documentation (#1044).

v0.9.12 (February 28, 2020)

  • Fixed writing NLPBlock in MathOptFormat (#1037).
  • Fixed MockOptimizer for result attributes with non-one result index (#1039).
  • Updated test template with instantiate (#1032).

v0.9.11 (February 21, 2020)

  • Add an option for the model created by Utilities.@model to be a subtype of AbstractOptimizer (#1031).
  • Described dual cone in docstrings of GeoMeanCone and RelativeEntropyCone (#1018, #1028).
  • Fixed typos in documentation (#1022, #1024).
  • Fixed warning of unsupported attribute (#1027).
  • Added more rootdet/logdet conic tests (#1026).
  • Implemented ConstraintDual for Constraint.GeoMeanBridge, Constraint.RootDetBridge and Constraint.LogDetBridge and test duals in tests with GeoMeanCone and RootDetConeTriangle and LogDetConeTriangle cones (#1025, #1026).

v0.9.10 (January 31, 2020)

  • Added OptimizerWithAttributes grouping an optimizer constructor and a list of optimizer attributes (#1008).
  • Added RelativeEntropyCone with corresponding bridge into exponential cone constraints (#993).
  • Added NormSpectralCone and NormNuclearCone with corresponding bridges into positive semidefinite constraints (#976).
  • Added supports_constrained_variable(s) (#1004).
  • Added dual_set_type (#1002).
  • Added tests for vector specialized version of delete (#989, #1011).
  • Added PSD3 test (#1007).
  • Clarified dual solution of Tests.pow1v and Tests.pow1f (#1013).
  • Added support for EqualTo and Zero in Bridges.Constraint.SplitIntervalBridge (#1005).
  • Fixed Utilities.vectorize for empty vector (#1003).
  • Fixed free variables in LP writer (#1006).

v0.9.9 (December 29, 2019)

  • Incorporated MathOptFormat.jl as the FileFormats submodule. FileFormats provides readers and writers for a number of standard file formats and MOF, a file format specialized for MOI (#969).
  • Improved performance of deletion of vector of variables in MOI.Utilities.Model (#983).
  • Updated to MutableArithmetics v0.2 (#981).
  • Added MutableArithmetics.promote_operation allocation tests (#975).
  • Fixed inference issue on Julia v1.1 (#982).

v0.9.8 (December 19, 2019)

  • Implemented MutableArithmetics API (#924).
  • Fixed callbacks with CachingOptimizer (#959).
  • Fixed MOI.dimension for MOI.Complements (#948).
  • Added fallback for add_variables (#972).
  • Added is_diagonal_vectorized_index utility (#965).
  • Improved linear constraints display in manual (#963, #964).
  • Bridges improvements:
    • Added IndicatorSet to SOS1 bridge (#877).
    • Added support for starting values for Variable.VectorizeBridge (#944).
    • Fixed MOI.add_constraints with non-bridged variable constraint on bridged variable (#951).
    • Fixed corner cases and docstring of GeoMeanBridge (#961, #962, #966).
    • Fixed choice between variable or constraint bridges for constrained variables (#973).
    • Improve performance of bridge shortest path (#945, #946, #956).
    • Added docstring for test_delete_bridge (#954).
    • Added Variable bridge tests (#952).

v0.9.7 (October 30, 2019)

  • Implemented _result_index_field for NLPBlockDual (#934).
  • Fixed copy of model with starting values for vector constraints (#941).
  • Bridges improvements:
    • Improved performance of add_bridge and added has_bridge (#935).
    • Added AbstractSetMapBridge for bridges between sets S1, S2 such that there is a linear map A such that A*S1 = S2 (#933).
    • Added support for starting values for FlipSignBridge, VectorizeBridge, ScalarizeBridge, SlackBridge, SplitIntervalBridge, RSOCBridge, SOCRBridge NormInfinityBridge, SOCtoPSDBridge and RSOCtoPSDBridge (#933, #936, #937, #938, #939).

v0.9.6 (October 25, 2019)

  • Added complementarity constraints (#913).
  • Allowed ModelLike objects as value of attributes (#928).
  • Testing improvements:
    • Added dual_objective_value option to MOI.Test.TestConfig (#922).
    • Added InvalidIndex tests in basic_constraint_tests (#921).
    • Added tests for the constant term in indicator constraint (#929).
  • Bridges improvements:
    • Added support for starting values for Functionize bridges (#923).
    • Added variable indices context to variable bridges (#920).
    • Fixed a typo in printing o debug_supports (#927).

v0.9.5 (October 9, 2019)

  • Clarified PrimalStatus/DualStatus to be NO_SOLUTION if result_index is out of bounds (#912).
  • Added tolerance for checks and use ResultCount + 1 for the result_index in MOI.Test.solve_result_status (#910, #917).
  • Use 0.5 instead of 2.0 for power in PowerCone in basic_constraint_test (#916).
  • Bridges improvements:
    • Added debug utilities for unsupported variable/constraint/objective (#861).
    • Fixed deletion of variables in bridged VectorOfVariables constraints (#909).
    • Fixed result_index with objective bridges (#911).

v0.9.4 (October 2, 2019)

  • Added solver-independent MIP callbacks (#782).
  • Implements submit for Utilities.CachingOptimizer and Bridges.AbstractBridgeOptimizer (#906).
  • Added tests for result count of solution attributes (#901, #904).
  • Added NumberOfThreads attribute (#892).
  • Added Utilities.get_bounds to get the bounds on a variable (#890).
  • Added a note on duplicate coefficients in documentation (#581).
  • Added result index in ConstraintBasisStatus (#898).
  • Added extension dictionary to Utilities.Model (#884, #895).
  • Fixed deletion of constrained variables for CachingOptimizer (#905).
  • Implemented Utilities.shift_constraint for Test.UnknownScalarSet (#896).
  • Bridges improvements:
    • Added Variable.RSOCtoSOCBridge (#907).
    • Implemented MOI.get for ConstraintFunction/ConstraintSet for Bridges.Constraint.SquareBridge (#899).

v0.9.3 (September 20, 2019)

  • Fixed ambiguity detected in Julia v1.3 (#891, #893).
  • Fixed missing sets from ListOfSupportedConstraints (#880).
  • Fixed copy of VectorOfVariables constraints with duplicate indices (#886).
  • Added extension dictionary to MOIU.Model (#884).
  • Implemented MOI.get for function and set for GeoMeanBridge (#888).
  • Updated documentation for SingleVariable indices and bridges (#885).
  • Testing improvements:
    • Added more comprehensive tests for names (#882).
    • Added tests for SingleVariable duals (#883).
    • Added tests for DualExponentialCone and DualPowerCone (#873).
  • Improvements for arbitrary coefficient type:
    • Fixed == for sets with mutable fields (#887).
    • Removed some Float64 assumptions in bridges (#878).
    • Automatic selection of Constraint.[Scalar|Vector]FunctionizeBridge (#889).

v0.9.2 (September 5, 2019)

  • Implemented model printing for MOI.ModelLike and specialized it for models defined in MOI (864).
  • Generalized contlinear tests for arbitrary coefficient type (#855).
  • Fixed supports_constraint for Semiinteger and Semicontinuous and supports for ObjectiveFunction (#859).
  • Fixed Allocate-Load copy for single variable constraints (#856).
  • Bridges improvements:
    • Add objective bridges (#789).
    • Fixed Variable.RSOCtoPSDBridge for dimension 2 (#869).
    • Added Variable.SOCtoRSOCBridge (#865).
    • Added Constraint.SOCRBridge and disable MOI.Bridges.Constraint.SOCtoPSDBridge (#751).
    • Fixed added_constraint_types for Contraint.LogDetBridge and Constraint.RootDetBridge (#870).

v0.9.1 (August 22, 2019)

  • Fix support for Julia v1.2 (#834).
  • L1 and L∞ norm epigraph cones and corresponding bridges to LP were added (#818).
  • Added tests to MOI.Test.nametest (#833).
  • Fix MOI.Test.soc3test for solvers not supporting infeasibility certificates (#839).
  • Implements operate for operators * and / between vector function and constant (#837).
  • Implements show for MOI.Utilities.IndexMap (#847).
  • Fix corner cases for mapping of variables in MOI.Utilities.CachingOptimizer and substitution of variables in MOI.Bridges.AbstractBridgeOptimizer (#848).
  • Fix transformation of constant terms for MOI.Bridges.Constraint.SOCtoPSDBridge and MOI.Bridges.Constraint.RSOCtoPSDBridge (#840).

v0.9.0 (August 13, 2019)

  • Support for Julia v0.6 and v0.7 was dropped (#714, #717).
  • A MOI.Utilities.Model implementation of ModelLike, this should replace most use cases of MOI.Utilities.@model (#781).
  • add_constrained_variable and add_constrained_variables were added (#759).
  • Support for indicator constraints was added (#709, #712).
  • DualObjectiveValue attribute was added (#473).
  • RawParameter attribute was added (#733).
  • A dual_set function was added (#804).
  • A Benchmarks submodule was added to facilitate solver benchmarking (#769).
  • A submit function was added, this may for instance allow the user to submit solutions or cuts to the solver from a callback (#775).
  • The field of ObjectiveValue was renamed to result_index (#729).
  • The _constant and Utilities.getconstant function were renamed to constant
  • REDUCTION_CERTIFICATE result status was added (#734).
  • Abstract matrix sets were added (#731).
  • Testing improvements:
    • The testing guideline was updated (#728).
    • Quadratic tests were added (#697).
    • Unit tests for RawStatusString, SolveTime, Silent and SolverName were added (#726, #741).
    • A rotated second-order cone test was added (#759).
    • A power cone test was added (#768).
    • Tests for ZeroOne variables with variable bounds were added (#772).
    • An unbounded test was added (#773).
    • Existing tests had a few updates (#702, #703, #763).
  • Documentation improvements:
    • Added a section on CachingOptimizer (#777).
    • Added a section on UniversalFallback, Model and @model (#762).
    • Transition the knapsack example to a doctest with MockOptimizer (#786).
  • Utilities improvements:
    • A CleverDict utility was added for a vector that automatically transform into a dictionary once a first index is removed (#767).
    • The Utilities.constant function was renamed to Utilities.constant_vector (#740).
    • Implement optimizer attributes for CachingOptimizer (#745).
    • Rename Utilities.add_scalar_constraint to Utilities.normalize_and_add_constraint (#801).
    • operate with vcat, SingleVariable and VectorOfVariables now returns a VectorOfVariables (#616).
    • Fix a type piracy of operate (#784).
    • The load_constraint fallback signature was fixed (#760).
    • The set_dot function was extended to work with sparse arrays (#805).
  • Bridges improvements:
    • The bridges no longer store the constraint function and set before it is bridged, the bridges now have to implement ConstraintFunction and ConstraintSet if the user wants to recover them. As a consequence, the @bridge macro was removed (#722).
    • Bridge are now instantiated with a bridge_constraint function instead of using a constructor (#730).
    • Fix constraint attributes for bridges (#699).
    • Constraint bridges were moved to the Bridges/Constraint submodule so they should now inherit from MOI.Bridges.Constraint.Abstract and should implement MOI.Bridges.Constraint.concrete_bridge_type instead of MOI.Bridges.concrete_bridge_type (#756).
    • Variable bridges were added in (#759).
    • Various improvements (#746, #747).

v0.8.4 (March 13, 2019)

  • Performance improvement in default_copy_to and bridge optimizer (#696).
  • Add Silent and implement setting optimizer attributes in caching and mock optimizers (#695).
  • Add Functionize bridges (SingleVariable and VectorOfVariables) (#659).
  • Minor typo fixes (#694).

v0.8.3 (March 6, 2019)

  • Use zero constant in scalar constraint function of MOI.Test.copytest (#691).
  • Fix variable deletion with SingleVariable objective function (#690).
  • Fix LazyBridgeOptimizer with bridges that add no constraints (#689).
  • Error message improvements (#673, #685, #686, #688).
  • Documentation improvements (#682, #683, #687).
  • Basis status:
    • Remove VariableBasisStatus (#679).
    • Test ConstraintBasisStatus and implement it in bridges (#678).
  • Fix inference of NumberOfVariables and NumberOfConstraints (#677).
  • Implement division between a quadratic function and a number (#675).

v0.8.2 (February 7, 2019)

  • Add RawStatusString attribute (#629).
  • Do not set names to the optimizer but only to the cache in CachingOptimizer (#638).
  • Make scalar MOI functions act as scalars in broadcast (#646).
  • Add function utilities:
    • Implement Base.zero (#634), Base.iszero (#643), add missing arithmetic operations (#644, #645) and fix division (#648).
    • Add a vectorize function that turns a vector of ScalarAffineFunction into a VectorAffineFunction (#642).
  • Improve support for starting values:
    • Show a warning in copy when starting values are not supported instead of throwing an error (#630).
    • Fix UniversalFallback for getting an variable or constraint attribute set to no indices (#623).
    • Add a test in contlineartest with partially set VariablePrimalStart.
  • Bridges improvements:
    • Fix StackOverFlow in LazyBridgeOptimizer when there is a cycle in the graph of bridges.
    • Add Slack bridges (#610, #650).
    • Add FlipSign bridges (#658).
  • Add tests with duplicate coefficients in ScalarAffineFunction and VectorAffineFunction (#639).
  • Use tolerance to compare VariablePrimal in rotatedsoc1 test (#632).
  • Use a zero constant in ScalarAffineFunction of constraints in psdt2 (#622).

v0.8.1 (January 7, 2019)

  • Adding an NLP objective now overrides any objective set using the ObjectiveFunction attribute (#619).
  • Rename fullbridgeoptimizer into full_bridge_optimizer (#621).
  • Allow custom constraint types with full_bridge_optimizer (#617).
  • Add Vectorize bridge which transforms scalar linear constraints into vector linear constraints (#615).

v0.8.0 (December 18, 2018)

  • Rename all enum values to follow the JuMP naming guidelines for constants, for example, Optimal becomes OPTIMAL, and DualInfeasible becomes DUAL_INFEASIBLE.
  • Rename CachingOptimizer methods for style compliance.
  • Add an MOI.TerminationStatusCode called ALMOST_DUAL_INFEASIBLE.

v0.7.0 (December 13, 2018)

  • Test that MOI.TerminationStatus is MOI.OptimizeNotCalled before MOI.optimize! is called.
  • Check supports_default_copy_to in tests (#594).
  • Key pieces of information like optimality, infeasibility, etc., are now reported through TerminationStatusCode. It is typically no longer necessary to check the result statuses in addition to the termination status.
  • Add perspective dimension to log-det cone (#593).

v0.6.4 (November 27, 2018)

  • Add OptimizeNotCalled termination status (#577) and improve documentation of other statuses (#575).
  • Add a solver naming guideline (#578).
  • Make FeasibilitySense the default ObjectiveSense (#579).
  • Fix Utilities.@model and Bridges.@bridge macros for functions and sets defined outside MOI (#582).
  • Document solver-specific attributes (#580) and implement them in Utilities.CachingOptimizer (#565).

v0.6.3 (November 16, 2018)

  • Variables and constraints are now allowed to have duplicate names. An error is thrown only on lookup. This change breaks some existing tests. (#549)
  • Attributes may now be partially set (some values could be nothing). (#563)
  • Performance improvements in Utilities.Model (#549, #567, #568)
  • Fix bug in QuadtoSOC (#558).
  • New supports_default_copy_to method that optimizers should implement to control caching behavior.
  • Documentation improvements.

v0.6.2 (October 26, 2018)

  • Improve hygiene of @model macro (#544).
  • Fix bug in copy tests (#543).
  • Fix bug in UniversalFallback attribute getter (#540).
  • Allow all correct solutions for solve_blank_obj unit test (#537).
  • Add errors for Allocate-Load and bad constraints (#534).
  • [performance] Add specialized implementation of hash for VariableIndex (#533).
  • [performance] Construct the name to object dictionaries lazily in model (#535).
  • Add the QuadtoSOC bridge which transforms ScalarQuadraticFunction constraints into RotatedSecondOrderCone (#483).

v0.6.1 (September 22, 2018)

  • Enable PositiveSemidefiniteConeSquare set and quadratic functions in MOIB.fullbridgeoptimizer (#524).
  • Add warning in the bridge between PositiveSemidefiniteConeSquare and PositiveSemidefiniteConeTriangle when the matrix is almost symmetric (#522).
  • Modify MOIT.copytest to not add multiples constraints on the same variable (#521).
  • Add missing keyword argument in one of MOIU.add_scalar_constraint methods (#520).

v0.6.0 (August 30, 2018)

  • The MOIU.@model and MOIB.@bridge macros now support functions and sets defined in external modules. As a consequence, function and set names in the macro arguments need to be prefixed by module name.
  • Rename functions according to the JuMP style guide:
    • copy! with keyword arguments copynames and warnattributes -> copy_to with keyword arguments copy_names and warn_attributes;
    • set! -> set;
    • addvariable[s]! -> add_variable[s];
    • supportsconstraint -> supports_constraint;
    • addconstraint[s]! -> add_constraint[s];
    • isvalid -> is_valid;
    • isempty -> is_empty;
    • Base.delete! -> delete;
    • modify! -> modify;
    • transform! -> transform;
    • initialize! -> initialize;
    • write -> write_to_file; and
    • read! -> read_from_file.
  • Remove free! (use Base.finalize instead).
  • Add the SquarePSD bridge which transforms PositiveSemidefiniteConeTriangle constraints into PositiveSemidefiniteConeTriangle.
  • Add result fallback for ConstraintDual of variable-wise constraint, ConstraintPrimal and ObjectiveValue.
  • Add tests for ObjectiveBound.
  • Add test for empty rows in vector linear constraint.
  • Rework errors: CannotError has been renamed NotAllowedError and the distinction between UnsupportedError and NotAllowedError is now about whether the element is not supported (for example, it cannot be copied a model containing this element) or the operation is not allowed (either because it is not implemented, because it cannot be performed in the current state of the model, or because it cannot be performed for a specific index)
  • canget is removed. NoSolution is added as a result status to indicate that the solver does not have either a primal or dual solution available (See #479).

v0.5.0 (August 5, 2018)

  • Fix names with CachingOptimizer.
  • Cleanup thanks to @mohamed82008.
  • Added a universal fallback for constraints.
  • Fast utilities for function canonicalization thanks to @rdeits.
  • Renamed dimension field to side_dimension in the context of matrix-like sets.
  • New and improved tests for cases like duplicate terms and ObjectiveBound.
  • Removed cantransform, canaddconstraint, canaddvariable, canset, canmodify, and candelete functions from the API. They are replaced by a new set of errors that are thrown: Subtypes of UnsupportedError indicate unsupported operations, while subtypes of CannotError indicate operations that cannot be performed in the current state.
  • The API for copy! is updated to remove the CopyResult type.
  • Updates for the new JuMP style guide.

v0.4.1 (June 28, 2018)

  • Fixes vector function modification on 32 bits.
  • Fixes Bellman-Ford algorithm for bridges.
  • Added an NLP test with FeasibilitySense.
  • Update modification documentation.

v0.4.0 (June 23, 2018)

  • Helper constructors for VectorAffineTerm and VectorQuadraticTerm.
  • Added modify_lhs to TestConfig.
  • Additional unit tests for optimizers.
  • Added a type parameter to CachingOptimizer for the optimizer field.
  • New API for problem modification (#388)
  • Tests pass without deprecation warnings on Julia 0.7.
  • Small fixes and documentation updates.

v0.3.0 (May 25, 2018)

  • Functions have been redefined to use arrays-of-structs instead of structs-of-arrays.
  • Improvements to MockOptimizer.
  • Significant changes to Bridges.
  • New and improved unit tests.
  • Fixes for Julia 0.7.

v0.2.0 (April 24, 2018)

  • Improvements to and better coverage of Tests.
  • Documentation fixes.
  • SolverName attribute.
  • Changes to the NLP interface (new definition of variable order and arrays of structs for bound pairs and sparsity patterns).
  • Addition of NLP tests.
  • Introduction of UniversalFallback.
  • copynames keyword argument to MOI.copy!.
  • Add Bridges submodule.

v0.1.0 (February 28, 2018)

  • Initial public release.
  • The framework for MOI was developed at the JuMP-dev workshop at MIT in June 2017 as a sorely needed replacement for MathProgBase.
+end

v0.9.22 (May 22, 2021)

This release contains backports from the ongoing development of the v0.10 release.

  • Improved type inference in Utilities, Bridges and FileFormats submodules to reduce latency.
  • Improved performance of Utilities.is_canonical.
  • Fixed Utilities.pass_nonvariable_constraints with bridged variables.
  • Fixed performance regression of Utilities.Model.
  • Fixed ordering of objective setting in parser.

v0.9.21 (April 23, 2021)

  • Added supports_shift_constant.
  • Improve performance of bridging quadratic constraints.
  • Add precompilation statements.
  • Large improvements to the documentation.
  • Fix a variety of inference issues, benefiting precompilation and reducing initial latency.
  • RawParameters are now ignored when resetting a CachingOptimizer. Previously, changing the underlying optimizer after RawParameters were set would throw an error.
  • Utilities.AbstractModel is being refactored. This may break users interacting with private fields of a model generated using @model.

v0.9.20 (February 20, 2021)

  • Improved performance of Utilities.ScalarFunctionIterator
  • Added support for compute_conflict to MOI layers
  • Added test with zero off-diagonal quadratic term in objective
  • Fixed double deletion of nested bridged SingleVariable/VectorOfVariables constraints
  • Fixed modification of un-set objective
  • Fixed function modification with duplicate terms
  • Made unit tests abort without failing if the problem class is not supported
  • Formatted code with JuliaFormatter
  • Clarified BasisStatusCode's docstring

v0.9.19 (December 1, 2020)

  • Added CallbackNodeStatus attribute
  • Added bridge from GreaterThan or LessThan to Interval
  • Added tests for infeasibility certificates and double optimize
  • Fixed support for Julia v1.6
  • Re-organized MOI docs and added documentation for adding a test

v0.9.18 (November 3, 2020)

  • Various improvements for working with complex numbers
  • Added GeoMeantoRelEntrBridge to bridge a GeometricMeanCone constraint to a relative entropy constraint

v0.9.17 (September 21, 2020)

  • Fixed CleverDict with variable of negative index value
  • Implement supports_add_constrained_variable for MockOptimizer

v0.9.16 (September 17, 2020)

  • Various fixes:
    • 32-bit support
    • CleverDict with abstract value type
    • Checks in test suite

v0.9.15 (September 14, 2020)

  • Bridges improvements:
    • (R)SOCtoNonConvexQuad bridge
    • ZeroOne bridge
    • Use supports_add_constrained_variable in LazyBridgeOptimizer
    • Exposed VariableBridgeCost and ConstraintBridgeCost attributes
    • Prioritize constraining variables on creation according to these costs
    • Refactor bridge debugging
  • Large performance improvements across all submodules
  • Lots of documentation improvements
  • FileFormats improvements:
    • Update MathOptFormat to v0.5
    • Fix supported objectives in FileFormats
  • Testing improvements:
    • Add name option for basic_constraint_test
  • Bug fixes and missing methods
    • Add length for iterators
    • Fix bug with duplicate terms
    • Fix order of LinearOfConstraintIndices

v0.9.14 (May 30, 2020)

  • Add a solver-independent interface for accessing the set of conflicting constraints an Irreducible Inconsistent Subsystem (#1056).
  • Bump JSONSchema dependency from v0.2 to v0.3 (#1090).
  • Documentation improvements:
    • Fix typos (#1054, #1060, #1061, #1064, #1069, #1070).
    • Remove the outdated recommendation for a package implementing MOI for a solver XXX to be called MathOptInterfaceXXX (#1087).
  • Utilities improvements:
    • Fix is_canonical for quadratic functions (#1081, #1089).
    • Implement add_constrained_variable[s] for CachingOptimizer so that it is added as constrained variables to the underlying optimizer (#1084).
    • Add support for custom objective functions for UniversalFallback (#1086).
    • Deterministic ordering of constraints in UniversalFallback (#1088).
  • Testing improvements:
    • Add NormOneCone/NormInfinityCone tests (#1045).
  • Bridges improvements:
    • Add bridges from Semiinteger and Semicontinuous (#1059).
    • Implement getting ConstraintSet for Variable.FlipSignBridge (#1066).
    • Fix setting ConstraintFunction for Constraint.ScalarizeBridge (#1093).
    • Fix NormOne/NormInf bridges with nonzero constants (#1045).
    • Fix StackOverflow in debug (#1063).
  • FileFormats improvements:
    • [SDPA] Implement the extension for integer variables (#1079).
    • [SDPA] Ignore comments after m and nblocks and detect dat-s extension (#1077).
    • [SDPA] No scaling of off-diagonal coefficient (#1076).
    • [SDPA] Add missing negation of constant (#1075).

v0.9.13 (March 24, 2020)

  • Added tests for Semicontinuous and Semiinteger variables (#1033).
  • Added tests for using ExprGraphs from NLP evaluators (#1043).
  • Update version compatibilities of dependencies (#1034, #1051, #1052).
  • Fixed typos in documentation (#1044).

v0.9.12 (February 28, 2020)

  • Fixed writing NLPBlock in MathOptFormat (#1037).
  • Fixed MockOptimizer for result attributes with non-one result index (#1039).
  • Updated test template with instantiate (#1032).

v0.9.11 (February 21, 2020)

  • Add an option for the model created by Utilities.@model to be a subtype of AbstractOptimizer (#1031).
  • Described dual cone in docstrings of GeoMeanCone and RelativeEntropyCone (#1018, #1028).
  • Fixed typos in documentation (#1022, #1024).
  • Fixed warning of unsupported attribute (#1027).
  • Added more rootdet/logdet conic tests (#1026).
  • Implemented ConstraintDual for Constraint.GeoMeanBridge, Constraint.RootDetBridge and Constraint.LogDetBridge and test duals in tests with GeoMeanCone and RootDetConeTriangle and LogDetConeTriangle cones (#1025, #1026).

v0.9.10 (January 31, 2020)

  • Added OptimizerWithAttributes grouping an optimizer constructor and a list of optimizer attributes (#1008).
  • Added RelativeEntropyCone with corresponding bridge into exponential cone constraints (#993).
  • Added NormSpectralCone and NormNuclearCone with corresponding bridges into positive semidefinite constraints (#976).
  • Added supports_constrained_variable(s) (#1004).
  • Added dual_set_type (#1002).
  • Added tests for vector specialized version of delete (#989, #1011).
  • Added PSD3 test (#1007).
  • Clarified dual solution of Tests.pow1v and Tests.pow1f (#1013).
  • Added support for EqualTo and Zero in Bridges.Constraint.SplitIntervalBridge (#1005).
  • Fixed Utilities.vectorize for empty vector (#1003).
  • Fixed free variables in LP writer (#1006).

v0.9.9 (December 29, 2019)

  • Incorporated MathOptFormat.jl as the FileFormats submodule. FileFormats provides readers and writers for a number of standard file formats and MOF, a file format specialized for MOI (#969).
  • Improved performance of deletion of vector of variables in MOI.Utilities.Model (#983).
  • Updated to MutableArithmetics v0.2 (#981).
  • Added MutableArithmetics.promote_operation allocation tests (#975).
  • Fixed inference issue on Julia v1.1 (#982).

v0.9.8 (December 19, 2019)

  • Implemented MutableArithmetics API (#924).
  • Fixed callbacks with CachingOptimizer (#959).
  • Fixed MOI.dimension for MOI.Complements (#948).
  • Added fallback for add_variables (#972).
  • Added is_diagonal_vectorized_index utility (#965).
  • Improved linear constraints display in manual (#963, #964).
  • Bridges improvements:
    • Added IndicatorSet to SOS1 bridge (#877).
    • Added support for starting values for Variable.VectorizeBridge (#944).
    • Fixed MOI.add_constraints with non-bridged variable constraint on bridged variable (#951).
    • Fixed corner cases and docstring of GeoMeanBridge (#961, #962, #966).
    • Fixed choice between variable or constraint bridges for constrained variables (#973).
    • Improve performance of bridge shortest path (#945, #946, #956).
    • Added docstring for test_delete_bridge (#954).
    • Added Variable bridge tests (#952).

v0.9.7 (October 30, 2019)

  • Implemented _result_index_field for NLPBlockDual (#934).
  • Fixed copy of model with starting values for vector constraints (#941).
  • Bridges improvements:
    • Improved performance of add_bridge and added has_bridge (#935).
    • Added AbstractSetMapBridge for bridges between sets S1, S2 such that there is a linear map A such that A*S1 = S2 (#933).
    • Added support for starting values for FlipSignBridge, VectorizeBridge, ScalarizeBridge, SlackBridge, SplitIntervalBridge, RSOCBridge, SOCRBridge NormInfinityBridge, SOCtoPSDBridge and RSOCtoPSDBridge (#933, #936, #937, #938, #939).

v0.9.6 (October 25, 2019)

  • Added complementarity constraints (#913).
  • Allowed ModelLike objects as value of attributes (#928).
  • Testing improvements:
    • Added dual_objective_value option to MOI.Test.TestConfig (#922).
    • Added InvalidIndex tests in basic_constraint_tests (#921).
    • Added tests for the constant term in indicator constraint (#929).
  • Bridges improvements:
    • Added support for starting values for Functionize bridges (#923).
    • Added variable indices context to variable bridges (#920).
    • Fixed a typo in printing o debug_supports (#927).

v0.9.5 (October 9, 2019)

  • Clarified PrimalStatus/DualStatus to be NO_SOLUTION if result_index is out of bounds (#912).
  • Added tolerance for checks and use ResultCount + 1 for the result_index in MOI.Test.solve_result_status (#910, #917).
  • Use 0.5 instead of 2.0 for power in PowerCone in basic_constraint_test (#916).
  • Bridges improvements:
    • Added debug utilities for unsupported variable/constraint/objective (#861).
    • Fixed deletion of variables in bridged VectorOfVariables constraints (#909).
    • Fixed result_index with objective bridges (#911).

v0.9.4 (October 2, 2019)

  • Added solver-independent MIP callbacks (#782).
  • Implements submit for Utilities.CachingOptimizer and Bridges.AbstractBridgeOptimizer (#906).
  • Added tests for result count of solution attributes (#901, #904).
  • Added NumberOfThreads attribute (#892).
  • Added Utilities.get_bounds to get the bounds on a variable (#890).
  • Added a note on duplicate coefficients in documentation (#581).
  • Added result index in ConstraintBasisStatus (#898).
  • Added extension dictionary to Utilities.Model (#884, #895).
  • Fixed deletion of constrained variables for CachingOptimizer (#905).
  • Implemented Utilities.shift_constraint for Test.UnknownScalarSet (#896).
  • Bridges improvements:
    • Added Variable.RSOCtoSOCBridge (#907).
    • Implemented MOI.get for ConstraintFunction/ConstraintSet for Bridges.Constraint.SquareBridge (#899).

v0.9.3 (September 20, 2019)

  • Fixed ambiguity detected in Julia v1.3 (#891, #893).
  • Fixed missing sets from ListOfSupportedConstraints (#880).
  • Fixed copy of VectorOfVariables constraints with duplicate indices (#886).
  • Added extension dictionary to MOIU.Model (#884).
  • Implemented MOI.get for function and set for GeoMeanBridge (#888).
  • Updated documentation for SingleVariable indices and bridges (#885).
  • Testing improvements:
    • Added more comprehensive tests for names (#882).
    • Added tests for SingleVariable duals (#883).
    • Added tests for DualExponentialCone and DualPowerCone (#873).
  • Improvements for arbitrary coefficient type:
    • Fixed == for sets with mutable fields (#887).
    • Removed some Float64 assumptions in bridges (#878).
    • Automatic selection of Constraint.[Scalar|Vector]FunctionizeBridge (#889).

v0.9.2 (September 5, 2019)

  • Implemented model printing for MOI.ModelLike and specialized it for models defined in MOI (864).
  • Generalized contlinear tests for arbitrary coefficient type (#855).
  • Fixed supports_constraint for Semiinteger and Semicontinuous and supports for ObjectiveFunction (#859).
  • Fixed Allocate-Load copy for single variable constraints (#856).
  • Bridges improvements:
    • Add objective bridges (#789).
    • Fixed Variable.RSOCtoPSDBridge for dimension 2 (#869).
    • Added Variable.SOCtoRSOCBridge (#865).
    • Added Constraint.SOCRBridge and disable MOI.Bridges.Constraint.SOCtoPSDBridge (#751).
    • Fixed added_constraint_types for Contraint.LogDetBridge and Constraint.RootDetBridge (#870).

v0.9.1 (August 22, 2019)

  • Fix support for Julia v1.2 (#834).
  • L1 and L∞ norm epigraph cones and corresponding bridges to LP were added (#818).
  • Added tests to MOI.Test.nametest (#833).
  • Fix MOI.Test.soc3test for solvers not supporting infeasibility certificates (#839).
  • Implements operate for operators * and / between vector function and constant (#837).
  • Implements show for MOI.Utilities.IndexMap (#847).
  • Fix corner cases for mapping of variables in MOI.Utilities.CachingOptimizer and substitution of variables in MOI.Bridges.AbstractBridgeOptimizer (#848).
  • Fix transformation of constant terms for MOI.Bridges.Constraint.SOCtoPSDBridge and MOI.Bridges.Constraint.RSOCtoPSDBridge (#840).

v0.9.0 (August 13, 2019)

  • Support for Julia v0.6 and v0.7 was dropped (#714, #717).
  • A MOI.Utilities.Model implementation of ModelLike, this should replace most use cases of MOI.Utilities.@model (#781).
  • add_constrained_variable and add_constrained_variables were added (#759).
  • Support for indicator constraints was added (#709, #712).
  • DualObjectiveValue attribute was added (#473).
  • RawParameter attribute was added (#733).
  • A dual_set function was added (#804).
  • A Benchmarks submodule was added to facilitate solver benchmarking (#769).
  • A submit function was added, this may for instance allow the user to submit solutions or cuts to the solver from a callback (#775).
  • The field of ObjectiveValue was renamed to result_index (#729).
  • The _constant and Utilities.getconstant function were renamed to constant
  • REDUCTION_CERTIFICATE result status was added (#734).
  • Abstract matrix sets were added (#731).
  • Testing improvements:
    • The testing guideline was updated (#728).
    • Quadratic tests were added (#697).
    • Unit tests for RawStatusString, SolveTime, Silent and SolverName were added (#726, #741).
    • A rotated second-order cone test was added (#759).
    • A power cone test was added (#768).
    • Tests for ZeroOne variables with variable bounds were added (#772).
    • An unbounded test was added (#773).
    • Existing tests had a few updates (#702, #703, #763).
  • Documentation improvements:
    • Added a section on CachingOptimizer (#777).
    • Added a section on UniversalFallback, Model and @model (#762).
    • Transition the knapsack example to a doctest with MockOptimizer (#786).
  • Utilities improvements:
    • A CleverDict utility was added for a vector that automatically transform into a dictionary once a first index is removed (#767).
    • The Utilities.constant function was renamed to Utilities.constant_vector (#740).
    • Implement optimizer attributes for CachingOptimizer (#745).
    • Rename Utilities.add_scalar_constraint to Utilities.normalize_and_add_constraint (#801).
    • operate with vcat, SingleVariable and VectorOfVariables now returns a VectorOfVariables (#616).
    • Fix a type piracy of operate (#784).
    • The load_constraint fallback signature was fixed (#760).
    • The set_dot function was extended to work with sparse arrays (#805).
  • Bridges improvements:
    • The bridges no longer store the constraint function and set before it is bridged, the bridges now have to implement ConstraintFunction and ConstraintSet if the user wants to recover them. As a consequence, the @bridge macro was removed (#722).
    • Bridge are now instantiated with a bridge_constraint function instead of using a constructor (#730).
    • Fix constraint attributes for bridges (#699).
    • Constraint bridges were moved to the Bridges/Constraint submodule so they should now inherit from MOI.Bridges.Constraint.Abstract and should implement MOI.Bridges.Constraint.concrete_bridge_type instead of MOI.Bridges.concrete_bridge_type (#756).
    • Variable bridges were added in (#759).
    • Various improvements (#746, #747).

v0.8.4 (March 13, 2019)

  • Performance improvement in default_copy_to and bridge optimizer (#696).
  • Add Silent and implement setting optimizer attributes in caching and mock optimizers (#695).
  • Add Functionize bridges (SingleVariable and VectorOfVariables) (#659).
  • Minor typo fixes (#694).

v0.8.3 (March 6, 2019)

  • Use zero constant in scalar constraint function of MOI.Test.copytest (#691).
  • Fix variable deletion with SingleVariable objective function (#690).
  • Fix LazyBridgeOptimizer with bridges that add no constraints (#689).
  • Error message improvements (#673, #685, #686, #688).
  • Documentation improvements (#682, #683, #687).
  • Basis status:
    • Remove VariableBasisStatus (#679).
    • Test ConstraintBasisStatus and implement it in bridges (#678).
  • Fix inference of NumberOfVariables and NumberOfConstraints (#677).
  • Implement division between a quadratic function and a number (#675).

v0.8.2 (February 7, 2019)

  • Add RawStatusString attribute (#629).
  • Do not set names to the optimizer but only to the cache in CachingOptimizer (#638).
  • Make scalar MOI functions act as scalars in broadcast (#646).
  • Add function utilities:
    • Implement Base.zero (#634), Base.iszero (#643), add missing arithmetic operations (#644, #645) and fix division (#648).
    • Add a vectorize function that turns a vector of ScalarAffineFunction into a VectorAffineFunction (#642).
  • Improve support for starting values:
    • Show a warning in copy when starting values are not supported instead of throwing an error (#630).
    • Fix UniversalFallback for getting an variable or constraint attribute set to no indices (#623).
    • Add a test in contlineartest with partially set VariablePrimalStart.
  • Bridges improvements:
    • Fix StackOverFlow in LazyBridgeOptimizer when there is a cycle in the graph of bridges.
    • Add Slack bridges (#610, #650).
    • Add FlipSign bridges (#658).
  • Add tests with duplicate coefficients in ScalarAffineFunction and VectorAffineFunction (#639).
  • Use tolerance to compare VariablePrimal in rotatedsoc1 test (#632).
  • Use a zero constant in ScalarAffineFunction of constraints in psdt2 (#622).

v0.8.1 (January 7, 2019)

  • Adding an NLP objective now overrides any objective set using the ObjectiveFunction attribute (#619).
  • Rename fullbridgeoptimizer into full_bridge_optimizer (#621).
  • Allow custom constraint types with full_bridge_optimizer (#617).
  • Add Vectorize bridge which transforms scalar linear constraints into vector linear constraints (#615).

v0.8.0 (December 18, 2018)

  • Rename all enum values to follow the JuMP naming guidelines for constants, for example, Optimal becomes OPTIMAL, and DualInfeasible becomes DUAL_INFEASIBLE.
  • Rename CachingOptimizer methods for style compliance.
  • Add an MOI.TerminationStatusCode called ALMOST_DUAL_INFEASIBLE.

v0.7.0 (December 13, 2018)

  • Test that MOI.TerminationStatus is MOI.OptimizeNotCalled before MOI.optimize! is called.
  • Check supports_default_copy_to in tests (#594).
  • Key pieces of information like optimality, infeasibility, etc., are now reported through TerminationStatusCode. It is typically no longer necessary to check the result statuses in addition to the termination status.
  • Add perspective dimension to log-det cone (#593).

v0.6.4 (November 27, 2018)

  • Add OptimizeNotCalled termination status (#577) and improve documentation of other statuses (#575).
  • Add a solver naming guideline (#578).
  • Make FeasibilitySense the default ObjectiveSense (#579).
  • Fix Utilities.@model and Bridges.@bridge macros for functions and sets defined outside MOI (#582).
  • Document solver-specific attributes (#580) and implement them in Utilities.CachingOptimizer (#565).

v0.6.3 (November 16, 2018)

  • Variables and constraints are now allowed to have duplicate names. An error is thrown only on lookup. This change breaks some existing tests. (#549)
  • Attributes may now be partially set (some values could be nothing). (#563)
  • Performance improvements in Utilities.Model (#549, #567, #568)
  • Fix bug in QuadtoSOC (#558).
  • New supports_default_copy_to method that optimizers should implement to control caching behavior.
  • Documentation improvements.

v0.6.2 (October 26, 2018)

  • Improve hygiene of @model macro (#544).
  • Fix bug in copy tests (#543).
  • Fix bug in UniversalFallback attribute getter (#540).
  • Allow all correct solutions for solve_blank_obj unit test (#537).
  • Add errors for Allocate-Load and bad constraints (#534).
  • [performance] Add specialized implementation of hash for VariableIndex (#533).
  • [performance] Construct the name to object dictionaries lazily in model (#535).
  • Add the QuadtoSOC bridge which transforms ScalarQuadraticFunction constraints into RotatedSecondOrderCone (#483).

v0.6.1 (September 22, 2018)

  • Enable PositiveSemidefiniteConeSquare set and quadratic functions in MOIB.fullbridgeoptimizer (#524).
  • Add warning in the bridge between PositiveSemidefiniteConeSquare and PositiveSemidefiniteConeTriangle when the matrix is almost symmetric (#522).
  • Modify MOIT.copytest to not add multiples constraints on the same variable (#521).
  • Add missing keyword argument in one of MOIU.add_scalar_constraint methods (#520).

v0.6.0 (August 30, 2018)

  • The MOIU.@model and MOIB.@bridge macros now support functions and sets defined in external modules. As a consequence, function and set names in the macro arguments need to be prefixed by module name.
  • Rename functions according to the JuMP style guide:
    • copy! with keyword arguments copynames and warnattributes -> copy_to with keyword arguments copy_names and warn_attributes;
    • set! -> set;
    • addvariable[s]! -> add_variable[s];
    • supportsconstraint -> supports_constraint;
    • addconstraint[s]! -> add_constraint[s];
    • isvalid -> is_valid;
    • isempty -> is_empty;
    • Base.delete! -> delete;
    • modify! -> modify;
    • transform! -> transform;
    • initialize! -> initialize;
    • write -> write_to_file; and
    • read! -> read_from_file.
  • Remove free! (use Base.finalize instead).
  • Add the SquarePSD bridge which transforms PositiveSemidefiniteConeTriangle constraints into PositiveSemidefiniteConeTriangle.
  • Add result fallback for ConstraintDual of variable-wise constraint, ConstraintPrimal and ObjectiveValue.
  • Add tests for ObjectiveBound.
  • Add test for empty rows in vector linear constraint.
  • Rework errors: CannotError has been renamed NotAllowedError and the distinction between UnsupportedError and NotAllowedError is now about whether the element is not supported (for example, it cannot be copied a model containing this element) or the operation is not allowed (either because it is not implemented, because it cannot be performed in the current state of the model, or because it cannot be performed for a specific index)
  • canget is removed. NoSolution is added as a result status to indicate that the solver does not have either a primal or dual solution available (See #479).

v0.5.0 (August 5, 2018)

  • Fix names with CachingOptimizer.
  • Cleanup thanks to @mohamed82008.
  • Added a universal fallback for constraints.
  • Fast utilities for function canonicalization thanks to @rdeits.
  • Renamed dimension field to side_dimension in the context of matrix-like sets.
  • New and improved tests for cases like duplicate terms and ObjectiveBound.
  • Removed cantransform, canaddconstraint, canaddvariable, canset, canmodify, and candelete functions from the API. They are replaced by a new set of errors that are thrown: Subtypes of UnsupportedError indicate unsupported operations, while subtypes of CannotError indicate operations that cannot be performed in the current state.
  • The API for copy! is updated to remove the CopyResult type.
  • Updates for the new JuMP style guide.

v0.4.1 (June 28, 2018)

  • Fixes vector function modification on 32 bits.
  • Fixes Bellman-Ford algorithm for bridges.
  • Added an NLP test with FeasibilitySense.
  • Update modification documentation.

v0.4.0 (June 23, 2018)

  • Helper constructors for VectorAffineTerm and VectorQuadraticTerm.
  • Added modify_lhs to TestConfig.
  • Additional unit tests for optimizers.
  • Added a type parameter to CachingOptimizer for the optimizer field.
  • New API for problem modification (#388)
  • Tests pass without deprecation warnings on Julia 0.7.
  • Small fixes and documentation updates.

v0.3.0 (May 25, 2018)

  • Functions have been redefined to use arrays-of-structs instead of structs-of-arrays.
  • Improvements to MockOptimizer.
  • Significant changes to Bridges.
  • New and improved unit tests.
  • Fixes for Julia 0.7.

v0.2.0 (April 24, 2018)

  • Improvements to and better coverage of Tests.
  • Documentation fixes.
  • SolverName attribute.
  • Changes to the NLP interface (new definition of variable order and arrays of structs for bound pairs and sparsity patterns).
  • Addition of NLP tests.
  • Introduction of UniversalFallback.
  • copynames keyword argument to MOI.copy!.
  • Add Bridges submodule.

v0.1.0 (February 28, 2018)

  • Initial public release.
  • The framework for MOI was developed at the JuMP-dev workshop at MIT in June 2017 as a sorely needed replacement for MathProgBase.
diff --git a/dev/submodules/Benchmarks/overview/index.html b/dev/submodules/Benchmarks/overview/index.html index 1c806d35ce..b76cf4c7d1 100644 --- a/dev/submodules/Benchmarks/overview/index.html +++ b/dev/submodules/Benchmarks/overview/index.html @@ -17,4 +17,4 @@ MOI.Benchmarks.compare_against_baseline( suite, "current"; directory = "/tmp", verbose = true -)

This comparison will create a report detailing improvements and regressions.

+)

This comparison will create a report detailing improvements and regressions.

diff --git a/dev/submodules/Benchmarks/reference/index.html b/dev/submodules/Benchmarks/reference/index.html index 9f27c3c4a1..da72d73639 100644 --- a/dev/submodules/Benchmarks/reference/index.html +++ b/dev/submodules/Benchmarks/reference/index.html @@ -8,7 +8,7 @@ julia> MOI.Benchmarks.suite(; exclude = [r"delete"]) do return Gurobi.Optimizer() - endsource
MathOptInterface.Benchmarks.create_baselineFunction
create_baseline(suite, name::String; directory::String = ""; kwargs...)

Run all benchmarks in suite and save to files called name in directory.

Extra kwargs are based to BenchmarkTools.run.

Example

julia> import MathOptInterface as MOI
+       end
source
MathOptInterface.Benchmarks.create_baselineFunction
create_baseline(suite, name::String; directory::String = ""; kwargs...)

Run all benchmarks in suite and save to files called name in directory.

Extra kwargs are based to BenchmarkTools.run.

Example

julia> import MathOptInterface as MOI
 
 julia> import GLPK
 
@@ -19,7 +19,7 @@
            "glpk_master";
            directory = "/tmp",
            verbose = true,
-       )
source
MathOptInterface.Benchmarks.compare_against_baselineFunction
compare_against_baseline(
     suite, name::String; directory::String = "",
     report_filename::String = "report.txt"
 )

Run all benchmarks in suite and compare against files called name in directory that were created by a call to create_baseline.

A report summarizing the comparison is written to report_filename in directory.

Extra kwargs are based to BenchmarkTools.run.

Example

julia> import MathOptInterface as MOI
@@ -33,4 +33,4 @@
            "glpk_master";
            directory = "/tmp",
            verbose = true,
-       )
source
+ )source diff --git a/dev/submodules/Bridges/implementation/index.html b/dev/submodules/Bridges/implementation/index.html index dfb7971c25..54363b6d5a 100644 --- a/dev/submodules/Bridges/implementation/index.html +++ b/dev/submodules/Bridges/implementation/index.html @@ -29,4 +29,4 @@ Subject to: ScalarAffineFunction{Int64}-in-LessThan{Int64} - (0) - (1) x <= (-1) + (0) - (1) x <= (-1) diff --git a/dev/submodules/Bridges/list_of_bridges/index.html b/dev/submodules/Bridges/list_of_bridges/index.html index 379a055415..6a7c0ad0b3 100644 --- a/dev/submodules/Bridges/list_of_bridges/index.html +++ b/dev/submodules/Bridges/list_of_bridges/index.html @@ -1,13 +1,13 @@ -List of bridges · MathOptInterface

List of bridges

This section describes the Bridges.AbstractBridges that are implemented in MathOptInterface.

Constraint bridges

These bridges are subtypes of Bridges.Constraint.AbstractBridge.

MathOptInterface.Bridges.Constraint.AllDifferentToCountDistinctBridgeType
AllDifferentToCountDistinctBridge{T,F} <: Bridges.Constraint.AbstractBridge

AllDifferentToCountDistinctBridge implements the following reformulations:

  • $x \in \textsf{AllDifferent}(d)$ to $(n, x) \in \textsf{CountDistinct}(1+d)$ and $n = d$
  • $f(x) \in \textsf{AllDifferent}(d)$ to $(d, f(x)) \in \textsf{CountDistinct}(1+d)$

Source node

AllDifferentToCountDistinctBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

AllDifferentToCountDistinctBridge creates:

source
MathOptInterface.Bridges.Constraint.BinPackingToMILPBridgeType
BinPackingToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

BinPackingToMILPBridge implements the following reformulation:

  • $x \in BinPacking(c, w)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} +List of bridges · MathOptInterface

List of bridges

This section describes the Bridges.AbstractBridges that are implemented in MathOptInterface.

Constraint bridges

These bridges are subtypes of Bridges.Constraint.AbstractBridge.

MathOptInterface.Bridges.Constraint.AllDifferentToCountDistinctBridgeType
AllDifferentToCountDistinctBridge{T,F} <: Bridges.Constraint.AbstractBridge

AllDifferentToCountDistinctBridge implements the following reformulations:

  • $x \in \textsf{AllDifferent}(d)$ to $(n, x) \in \textsf{CountDistinct}(1+d)$ and $n = d$
  • $f(x) \in \textsf{AllDifferent}(d)$ to $(d, f(x)) \in \textsf{CountDistinct}(1+d)$

Source node

AllDifferentToCountDistinctBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

AllDifferentToCountDistinctBridge creates:

source
MathOptInterface.Bridges.Constraint.BinPackingToMILPBridgeType
BinPackingToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

BinPackingToMILPBridge implements the following reformulation:

  • $x \in BinPacking(c, w)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} z_{ij} \in \{0, 1\} & \;\; \forall i \in 1\ldots d, j \in S_i \\ x_i - \sum\limits_{j\in S_i} j \cdot z_{ij} = 0 & \;\; \forall i \in 1\ldots d \\ \sum\limits_{j\in S_i} z_{ij} = 1 & \;\; \forall i \in 1\ldots d \\ -\end{aligned}\]

Then, we add the capacity constraint for all possible bins $j$:

\[\sum\limits_{i} w_i z_{ij} \le c \forall j \in \bigcup_{i=1,\ldots,d} S_i\]

Source node

BinPackingToMILPBridge supports:

Target nodes

BinPackingToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CircuitToMILPBridgeType
CircuitToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CircuitToMILPBridge implements the following reformulation:

  • $x \in \textsf{Circuit}(d)$ to the Miller-Tucker-Zemlin formulation of the Traveling Salesperson Problem.

Source node

CircuitToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CircuitToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridgeType
ComplexNormInfinityToSecondOrderConeBridge{T} <: Bridges.Constraint.AbstractBridge

ComplexNormInfinityToSecondOrderConeBridge implements the following reformulation:

  • $(t, x) \in NormInfinity(1+d)$ into $(t, real(x_i), imag(x_i)) \in SecondOrderCone()$ for all $i$.

Source node

ComplexNormInfinityToSecondOrderConeBridge supports:

Target nodes

ComplexNormInfinityToSecondOrderConeBridge creates:

source
MathOptInterface.Bridges.Constraint.CountAtLeastToCountBelongsBridgeType
CountAtLeastToCountBelongsBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountAtLeastToCountBelongsBridge implements the following reformulation:

  • $x \in \textsf{CountAtLeast}(n, d, \mathcal{S})$ to $(n_i, x_{d_i}) \in \textsf{CountBelongs}(1+d, \mathcal{S})$ and $n_i \ge n$ for all $i$.

Source node

CountAtLeastToCountBelongsBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountAtLeastToCountBelongsBridge creates:

source
MathOptInterface.Bridges.Constraint.CountBelongsToMILPBridgeType
CountBelongsToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountBelongsToMILPBridge implements the following reformulation:

  • $(n, x) \in \textsf{CountBelongs}(1+d, \mathcal{S})$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} +\end{aligned}\]

Then, we add the capacity constraint for all possible bins $j$:

\[\sum\limits_{i} w_i z_{ij} \le c \forall j \in \bigcup_{i=1,\ldots,d} S_i\]

Source node

BinPackingToMILPBridge supports:

Target nodes

BinPackingToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CircuitToMILPBridgeType
CircuitToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CircuitToMILPBridge implements the following reformulation:

  • $x \in \textsf{Circuit}(d)$ to the Miller-Tucker-Zemlin formulation of the Traveling Salesperson Problem.

Source node

CircuitToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CircuitToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ComplexNormInfinityToSecondOrderConeBridgeType
ComplexNormInfinityToSecondOrderConeBridge{T} <: Bridges.Constraint.AbstractBridge

ComplexNormInfinityToSecondOrderConeBridge implements the following reformulation:

  • $(t, x) \in NormInfinity(1+d)$ into $(t, real(x_i), imag(x_i)) \in SecondOrderCone()$ for all $i$.

Source node

ComplexNormInfinityToSecondOrderConeBridge supports:

Target nodes

ComplexNormInfinityToSecondOrderConeBridge creates:

source
MathOptInterface.Bridges.Constraint.CountAtLeastToCountBelongsBridgeType
CountAtLeastToCountBelongsBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountAtLeastToCountBelongsBridge implements the following reformulation:

  • $x \in \textsf{CountAtLeast}(n, d, \mathcal{S})$ to $(n_i, x_{d_i}) \in \textsf{CountBelongs}(1+d, \mathcal{S})$ and $n_i \ge n$ for all $i$.

Source node

CountAtLeastToCountBelongsBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountAtLeastToCountBelongsBridge creates:

source
MathOptInterface.Bridges.Constraint.CountBelongsToMILPBridgeType
CountBelongsToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountBelongsToMILPBridge implements the following reformulation:

  • $(n, x) \in \textsf{CountBelongs}(1+d, \mathcal{S})$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} z_{ij} \in \{0, 1\} & \;\; \forall i \in 1\ldots d, j \in S_i \\ x_i - \sum\limits_{j\in S_i} j \cdot z_{ij} = 0 & \;\; \forall i \in 1\ldots d \\ \sum\limits_{j\in S_i} z_{ij} = 1 & \;\; \forall i \in 1\ldots d \\ -\end{aligned}\]

Finally, $n$ is constrained to be the number of $z_{ij}$ elements that are in $\mathcal{S}$:

\[n - \sum\limits_{i\in 1\ldots d, j \in \mathcal{S}} z_{ij} = 0\]

Source node

CountBelongsToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountBelongsToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CountDistinctToMILPBridgeType
CountDistinctToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountDistinctToMILPBridge implements the following reformulation:

  • $(n, x) \in \textsf{CountDistinct}(1+d)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} +\end{aligned}\]

Finally, $n$ is constrained to be the number of $z_{ij}$ elements that are in $\mathcal{S}$:

\[n - \sum\limits_{i\in 1\ldots d, j \in \mathcal{S}} z_{ij} = 0\]

Source node

CountBelongsToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountBelongsToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CountDistinctToMILPBridgeType
CountDistinctToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountDistinctToMILPBridge implements the following reformulation:

  • $(n, x) \in \textsf{CountDistinct}(1+d)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} z_{ij} \in \{0, 1\} & \;\; \forall i \in 1\ldots d, j \in S_i \\ x_i - \sum\limits_{j\in S_i} j \cdot z_{ij} = 0 & \;\; \forall i \in 1\ldots d \\ \sum\limits_{j\in S_i} z_{ij} = 1 & \;\; \forall i \in 1\ldots d \\ @@ -18,8 +18,8 @@ z \in \{0, 1\} \\ x - y - M z \le -1 \\ y - x - M (1 - z) \le -1 -\end{aligned}\]

Source node

CountDistinctToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountDistinctToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CountGreaterThanToMILPBridgeType
CountGreaterThanToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountGreaterThanToMILPBridge implements the following reformulation:

  • $(c, y, x) \in CountGreaterThan()$ into a mixed-integer linear program.

Source node

CountGreaterThanToMILPBridge supports:

Target nodes

CountGreaterThanToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ExponentialConeToScalarNonlinearFunctionBridgeType
ExponentialConeToScalarNonlinearFunctionBridge{T,F} <:
-    Bridges.Constraint.AbstractBridge

ExponentialConeToScalarNonlinearFunctionBridge implements the following reformulation:

  • $(x, y, z) \in \textsf{ExponentialCone}()$ to $y \cdot exp(x / y)) - z \le 0$, $y \ge 0$.

Source node

ExponentialConeToScalarNonlinearFunctionBridge supports:

Target nodes

ExponentialConeToScalarNonlinearFunctionBridge creates:

source
MathOptInterface.Bridges.Constraint.FunctionConversionBridgeType
FunctionConversionBridge{T,F,G,S} <: AbstractFunctionConversionBridge{G,S}

FunctionConversionBridge implements the following reformulations:

  • $g(x) \in S$ into $f(x) \in S$

for these pairs of functions:

See also SetConversionBridge.

Source node

FunctionConversionBridge supports:

  • G in S

Target nodes

FunctionConversionBridge creates:

  • F in S
source
MathOptInterface.Bridges.Constraint.GeoMeanBridgeType
GeoMeanBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

GeoMeanBridge implements a reformulation from MOI.GeometricMeanCone into MOI.RotatedSecondOrderCone.

The reformulation is best described in an example.

Consider the cone of dimension 4:

\[t \le \sqrt[3]{x_1 x_2 x_3}\]

This can be rewritten as $\exists y \ge 0$ such that:

\[\begin{align*} +\end{aligned}\]

Source node

CountDistinctToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

CountDistinctToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.CountGreaterThanToMILPBridgeType
CountGreaterThanToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

CountGreaterThanToMILPBridge implements the following reformulation:

  • $(c, y, x) \in CountGreaterThan()$ into a mixed-integer linear program.

Source node

CountGreaterThanToMILPBridge supports:

Target nodes

CountGreaterThanToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ExponentialConeToScalarNonlinearFunctionBridgeType
ExponentialConeToScalarNonlinearFunctionBridge{T,F} <:
+    Bridges.Constraint.AbstractBridge

ExponentialConeToScalarNonlinearFunctionBridge implements the following reformulation:

  • $(x, y, z) \in \textsf{ExponentialCone}()$ to $y \cdot exp(x / y)) - z \le 0$, $y \ge 0$.

Source node

ExponentialConeToScalarNonlinearFunctionBridge supports:

Target nodes

ExponentialConeToScalarNonlinearFunctionBridge creates:

source
MathOptInterface.Bridges.Constraint.FunctionConversionBridgeType
FunctionConversionBridge{T,F,G,S} <: AbstractFunctionConversionBridge{G,S}

FunctionConversionBridge implements the following reformulations:

  • $g(x) \in S$ into $f(x) \in S$

for these pairs of functions:

See also SetConversionBridge.

Source node

FunctionConversionBridge supports:

  • G in S

Target nodes

FunctionConversionBridge creates:

  • F in S
source
MathOptInterface.Bridges.Constraint.GeoMeanBridgeType
GeoMeanBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

GeoMeanBridge implements a reformulation from MOI.GeometricMeanCone into MOI.RotatedSecondOrderCone.

The reformulation is best described in an example.

Consider the cone of dimension 4:

\[t \le \sqrt[3]{x_1 x_2 x_3}\]

This can be rewritten as $\exists y \ge 0$ such that:

\[\begin{align*} t & \le y,\\ y^4 & \le x_1 x_2 x_3 y. \end{align*}\]

Note that we need to create $y$ and not use $t^4$ directly because $t$ is not allowed to be negative.

This is equivalent to:

\[\begin{align*} @@ -28,7 +28,7 @@ y_2^2 & \le 2x_1 x_2, \\ y_3^2 & \le 2x_3(y_1/\sqrt{4}) \\ y & \ge 0. -\end{align*}\]

More generally, you can show how the geometric mean code is recursively expanded into a set of new variables $y$ in MOI.Nonnegatives, a set of MOI.RotatedSecondOrderCone constraints, and a MOI.LessThan constraint between $t$ and $y_1$.

Source node

GeoMeanBridge supports:

Target nodes

GeoMeanBridge creates:

source
MathOptInterface.Bridges.Constraint.GeoMeanToPowerBridgeType
GeoMeanToPowerBridge{T,F} <: Bridges.Constraint.AbstractBridge

GeoMeanToPowerBridge implements the following reformulation:

  • $(y, x...) \in GeometricMeanCone(1+d)$ into $(x_1, t, y) \in PowerCone(1/d)$ and $(t, x_2, ..., x_d) in GeometricMeanCone(d)$, which is then recursively expanded into more PowerCone constraints.

Source node

GeoMeanToPowerBridge supports:

Target nodes

GeoMeanToPowerBridge creates:

source
MathOptInterface.Bridges.Constraint.GeoMeantoRelEntrBridgeType
GeoMeantoRelEntrBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

GeoMeantoRelEntrBridge implements the following reformulation:

  • $(u, w) \in GeometricMeanCone$ into $(0, w, (u + y)\mathbf{1})\in RelativeEntropyCone$ and $y \ge 0$

Source node

GeoMeantoRelEntrBridge supports:

Target nodes

GeoMeantoRelEntrBridge creates:

Derivation

The derivation of the bridge is as follows:

\[\begin{aligned} +\end{align*}\]

More generally, you can show how the geometric mean code is recursively expanded into a set of new variables $y$ in MOI.Nonnegatives, a set of MOI.RotatedSecondOrderCone constraints, and a MOI.LessThan constraint between $t$ and $y_1$.

Source node

GeoMeanBridge supports:

Target nodes

GeoMeanBridge creates:

source
MathOptInterface.Bridges.Constraint.GeoMeanToPowerBridgeType
GeoMeanToPowerBridge{T,F} <: Bridges.Constraint.AbstractBridge

GeoMeanToPowerBridge implements the following reformulation:

  • $(y, x...) \in GeometricMeanCone(1+d)$ into $(x_1, t, y) \in PowerCone(1/d)$ and $(t, x_2, ..., x_d) in GeometricMeanCone(d)$, which is then recursively expanded into more PowerCone constraints.

Source node

GeoMeanToPowerBridge supports:

Target nodes

GeoMeanToPowerBridge creates:

source
MathOptInterface.Bridges.Constraint.GeoMeantoRelEntrBridgeType
GeoMeantoRelEntrBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

GeoMeantoRelEntrBridge implements the following reformulation:

  • $(u, w) \in GeometricMeanCone$ into $(0, w, (u + y)\mathbf{1})\in RelativeEntropyCone$ and $y \ge 0$

Source node

GeoMeantoRelEntrBridge supports:

Target nodes

GeoMeantoRelEntrBridge creates:

Derivation

The derivation of the bridge is as follows:

\[\begin{aligned} (u, w) \in GeometricMeanCone \iff & u \le \left(\prod_{i=1}^n w_i\right)^{1/n} \\ \iff & 0 \le u + y \le \left(\prod_{i=1}^n w_i\right)^{1/n}, y \ge 0 \\ \iff & 1 \le \frac{\left(\prod_{i=1}^n w_i\right)^{1/n}}{u + y}, y \ge 0 \\ @@ -37,7 +37,7 @@ \iff & 0 \ge \sum_{i=1}^n \log\left(\frac{u + y}{w_i}\right), y \ge 0 \\ \iff & 0 \ge \sum_{i=1}^n (u + y) \log\left(\frac{u + y}{w_i}\right), y \ge 0 \\ \iff & (0, w, (u + y)\mathbf{1}) \in RelativeEntropyCone, y \ge 0 \\ -\end{aligned}\]

This derivation assumes that $u + y > 0$, which is enforced by the relative entropy cone.

source
MathOptInterface.Bridges.Constraint.HermitianToSymmetricPSDBridgeType
HermitianToSymmetricPSDBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

HermitianToSymmetricPSDBridge implements the following reformulation:

  • Hermitian positive semidefinite n x n complex matrix to a symmetric positive semidefinite 2n x 2n real matrix.

See also MOI.Bridges.Variable.HermitianToSymmetricPSDBridge.

Source node

HermitianToSymmetricPSDBridge supports:

Target node

HermitianToSymmetricPSDBridge creates:

Reformulation

The reformulation is best described by example.

The Hermitian matrix:

\[\begin{bmatrix} +\end{aligned}\]

This derivation assumes that $u + y > 0$, which is enforced by the relative entropy cone.

source
MathOptInterface.Bridges.Constraint.HermitianToSymmetricPSDBridgeType
HermitianToSymmetricPSDBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

HermitianToSymmetricPSDBridge implements the following reformulation:

  • Hermitian positive semidefinite n x n complex matrix to a symmetric positive semidefinite 2n x 2n real matrix.

See also MOI.Bridges.Variable.HermitianToSymmetricPSDBridge.

Source node

HermitianToSymmetricPSDBridge supports:

Target node

HermitianToSymmetricPSDBridge creates:

Reformulation

The reformulation is best described by example.

The Hermitian matrix:

\[\begin{bmatrix} x_{11} & x_{12} + y_{12}im & x_{13} + y_{13}im\\ x_{12} - y_{12}im & x_{22} & x_{23} + y_{23}im\\ x_{13} - y_{13}im & x_{23} - y_{23}im & x_{33} @@ -48,7 +48,7 @@ & & & x_{11} & x_{12} & x_{13} \\ & & & & x_{22} & x_{23} \\ & & & & & x_{33} -\end{bmatrix}\]

is positive semidefinite.

The bridge achieves this reformulation by constraining the above matrix to belong to the MOI.PositiveSemidefiniteConeTriangle(6).

source
MathOptInterface.Bridges.Constraint.IndicatorActiveOnFalseBridgeType
IndicatorActiveOnFalseBridge{T,F,S} <: Bridges.Constraint.AbstractBridge

IndicatorActiveOnFalseBridge implements the following reformulation:

  • $\neg z \implies {f(x) \in S}$ into $y \implies {f(x) \in S}$, $z + y = 1$, and $y \in \{0, 1\}$

Source node

IndicatorActiveOnFalseBridge supports:

Target nodes

IndicatorActiveOnFalseBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorGreaterToLessThanBridgeType
IndicatorGreaterToLessThanBridge{T,A} <: Bridges.Constraint.AbstractBridge

IndicatorGreaterToLessThanBridge implements the following reformulation:

  • $z \implies {f(x) \ge l}$ into $z \implies {-f(x) \le -l}$

Source node

IndicatorGreaterToLessThanBridge supports:

Target nodes

IndicatorGreaterToLessThanBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorLessToGreaterThanBridgeType
IndicatorLessToGreaterThanBridge{T,A} <: Bridges.Constraint.AbstractBridge

IndicatorLessToGreaterThanBridge implements the following reformulations:

  • $z \implies {f(x) \le u}$ into $z \implies {-f(x) \ge -u}$

Source node

IndicatorLessToGreaterThanBridge supports:

Target nodes

IndicatorLessToGreaterThanBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorSOS1BridgeType
IndicatorSOS1Bridge{T,S} <: Bridges.Constraint.AbstractBridge

IndicatorSOS1Bridge implements the following reformulation:

  • $z \implies {f(x) \in S}$ into $f(x) + y \in S$, $SOS1(y, z)$
Warning

This bridge assumes that the solver supports MOI.SOS1{T} constraints in which one of the variables ($y$) is continuous.

Source node

IndicatorSOS1Bridge supports:

Target nodes

IndicatorSOS1Bridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorSetMapBridgeType
IndicatorSetMapBridge{T,A,S1,S2} <: Bridges.Constraint.AbstractBridge

IndicatorSetMapBridge implements the following reformulations:

  • $z \implies {f(x) \ge l}$ into $z \implies {-f(x) \le -l}$
  • $z \implies {f(x) \le u}$ into $z \implies {-f(x) \ge -u}$

Source node

IndicatorSetMapBridge supports:

Target nodes

IndicatorSetMapBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorToMILPBridgeType
IndicatorToMILPBridge{T,F,A,S} <: Bridges.Constraint.AbstractBridge

IndicatorToMILPBridge implements the following reformulation:

  • $x \in \textsf{Indicator}(s)$ into a mixed-integer linear program.

Source node

IndicatorToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

IndicatorToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.InequalityToComplementsBridgeType
InequalityToComplementsBridge{T,F,S,G} <: Bridges.Constraint.AbstractBridge

InequalityToComplementsBridge implements the following reformulations:

  • $f(x) \ge b$ into $\exists y$ such that $f(x) - b \perp y \ge 0$
  • $f(x) \le b$ into $f(x) - b \perp y \le 0$
  • $f(x) = b$ into $f(x) - b \perp y$

Source node

InequalityToComplementsBridge supports:

Target nodes

InequalityToComplementsBridge creates:

source
MathOptInterface.Bridges.Constraint.IntegerToZeroOneBridgeType
IntegerToZeroOneBridge{T} <: Bridges.Constraint.AbstractBridge

IntegerToZeroOneBridge implements the following reformulation:

  • $x \in \mathbf{Z}$ into $y_i \in \{0, 1\}$, $x == lb + \sum 2^{i-1} y_i$.

Source node

IntegerToZeroOneBridge supports:

Target nodes

IntegerToZeroOneBridge creates:

Developer note

This bridge is implemented as a constraint bridge instead of a variable bridge because we don't want to substitute the linear combination of y for every instance of x. Doing so would be expensive and greatly reduce the sparsity of the constraints.

source
MathOptInterface.Bridges.Constraint.LogDetBridgeType
LogDetBridge{T,F,G,H,I} <: Bridges.Constraint.AbstractBridge

The MOI.LogDetConeTriangle is representable by MOI.PositiveSemidefiniteConeTriangle and MOI.ExponentialCone constraints.

Indeed, $\log\det(X) = \sum\limits_{i=1}^n \log(\delta_i)$ where $\delta_i$ are the eigenvalues of $X$.

Adapting the method from [1, p. 149], we see that $t \le u \log(\det(X/u))$ for $u > 0$ if and only if there exists a lower triangular matrix $Δ$ such that

\[\begin{align*} +\end{bmatrix}\]

is positive semidefinite.

The bridge achieves this reformulation by constraining the above matrix to belong to the MOI.PositiveSemidefiniteConeTriangle(6).

source
MathOptInterface.Bridges.Constraint.IndicatorActiveOnFalseBridgeType
IndicatorActiveOnFalseBridge{T,F,S} <: Bridges.Constraint.AbstractBridge

IndicatorActiveOnFalseBridge implements the following reformulation:

  • $\neg z \implies {f(x) \in S}$ into $y \implies {f(x) \in S}$, $z + y = 1$, and $y \in \{0, 1\}$

Source node

IndicatorActiveOnFalseBridge supports:

Target nodes

IndicatorActiveOnFalseBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorGreaterToLessThanBridgeType
IndicatorGreaterToLessThanBridge{T,A} <: Bridges.Constraint.AbstractBridge

IndicatorGreaterToLessThanBridge implements the following reformulation:

  • $z \implies {f(x) \ge l}$ into $z \implies {-f(x) \le -l}$

Source node

IndicatorGreaterToLessThanBridge supports:

Target nodes

IndicatorGreaterToLessThanBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorLessToGreaterThanBridgeType
IndicatorLessToGreaterThanBridge{T,A} <: Bridges.Constraint.AbstractBridge

IndicatorLessToGreaterThanBridge implements the following reformulations:

  • $z \implies {f(x) \le u}$ into $z \implies {-f(x) \ge -u}$

Source node

IndicatorLessToGreaterThanBridge supports:

Target nodes

IndicatorLessToGreaterThanBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorSOS1BridgeType
IndicatorSOS1Bridge{T,S} <: Bridges.Constraint.AbstractBridge

IndicatorSOS1Bridge implements the following reformulation:

  • $z \implies {f(x) \in S}$ into $f(x) + y \in S$, $SOS1(y, z)$
Warning

This bridge assumes that the solver supports MOI.SOS1{T} constraints in which one of the variables ($y$) is continuous.

Source node

IndicatorSOS1Bridge supports:

Target nodes

IndicatorSOS1Bridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorSetMapBridgeType
IndicatorSetMapBridge{T,A,S1,S2} <: Bridges.Constraint.AbstractBridge

IndicatorSetMapBridge implements the following reformulations:

  • $z \implies {f(x) \ge l}$ into $z \implies {-f(x) \le -l}$
  • $z \implies {f(x) \le u}$ into $z \implies {-f(x) \ge -u}$

Source node

IndicatorSetMapBridge supports:

Target nodes

IndicatorSetMapBridge creates:

source
MathOptInterface.Bridges.Constraint.IndicatorToMILPBridgeType
IndicatorToMILPBridge{T,F,A,S} <: Bridges.Constraint.AbstractBridge

IndicatorToMILPBridge implements the following reformulation:

  • $x \in \textsf{Indicator}(s)$ into a mixed-integer linear program.

Source node

IndicatorToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

IndicatorToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.InequalityToComplementsBridgeType
InequalityToComplementsBridge{T,F,S,G} <: Bridges.Constraint.AbstractBridge

InequalityToComplementsBridge implements the following reformulations:

  • $f(x) \ge b$ into $\exists y$ such that $f(x) - b \perp y \ge 0$
  • $f(x) \le b$ into $f(x) - b \perp y \le 0$
  • $f(x) = b$ into $f(x) - b \perp y$

Source node

InequalityToComplementsBridge supports:

Target nodes

InequalityToComplementsBridge creates:

source
MathOptInterface.Bridges.Constraint.IntegerToZeroOneBridgeType
IntegerToZeroOneBridge{T} <: Bridges.Constraint.AbstractBridge

IntegerToZeroOneBridge implements the following reformulation:

  • $x \in \mathbf{Z}$ into $y_i \in \{0, 1\}$, $x == lb + \sum 2^{i-1} y_i$.

Source node

IntegerToZeroOneBridge supports:

Target nodes

IntegerToZeroOneBridge creates:

Developer note

This bridge is implemented as a constraint bridge instead of a variable bridge because we don't want to substitute the linear combination of y for every instance of x. Doing so would be expensive and greatly reduce the sparsity of the constraints.

source
MathOptInterface.Bridges.Constraint.LogDetBridgeType
LogDetBridge{T,F,G,H,I} <: Bridges.Constraint.AbstractBridge

The MOI.LogDetConeTriangle is representable by MOI.PositiveSemidefiniteConeTriangle and MOI.ExponentialCone constraints.

Indeed, $\log\det(X) = \sum\limits_{i=1}^n \log(\delta_i)$ where $\delta_i$ are the eigenvalues of $X$.

Adapting the method from [1, p. 149], we see that $t \le u \log(\det(X/u))$ for $u > 0$ if and only if there exists a lower triangular matrix $Δ$ such that

\[\begin{align*} \begin{pmatrix} X & Δ\\ Δ^\top & \mathrm{Diag}(Δ) @@ -61,7 +61,7 @@ \end{pmatrix} & \succeq 0\\ (l_i, u , Δ_{ii}) & \in ExponentialCone\quad \forall i \\ t - \sum_{i=1}^n l_i & \le 0 -\end{align*}\]

Source node

LogDetBridge supports:

Target nodes

LogDetBridge creates:

[1] Ben-Tal, Aharon, and Arkadi Nemirovski. Lectures on modern convex optimization: analysis, algorithms, and engineering applications. Society for Industrial and Applied Mathematics, 2001.

source
MathOptInterface.Bridges.Constraint.MultiSetMapBridgeType
abstract type MultiSetMapBridge{T,S1,G} <: AbstractBridge end

Same as SetMapBridge but the output constraint type does not only depend on the input constraint type.

When subtyping MultiSetMapBridge, added_constraint_types and supports should additionally be implemented by the bridge.

For example, if a bridge BridgeType may create either a constraint of type F2-in-S2 or F3-in-S3, these methods should be implemented as follows:

function MOI.Bridges.added_constraint_types(
+\end{align*}\]

Source node

LogDetBridge supports:

Target nodes

LogDetBridge creates:

[1] Ben-Tal, Aharon, and Arkadi Nemirovski. Lectures on modern convex optimization: analysis, algorithms, and engineering applications. Society for Industrial and Applied Mathematics, 2001.

source
MathOptInterface.Bridges.Constraint.MultiSetMapBridgeType
abstract type MultiSetMapBridge{T,S1,G} <: AbstractBridge end

Same as SetMapBridge but the output constraint type does not only depend on the input constraint type.

When subtyping MultiSetMapBridge, added_constraint_types and supports should additionally be implemented by the bridge.

For example, if a bridge BridgeType may create either a constraint of type F2-in-S2 or F3-in-S3, these methods should be implemented as follows:

function MOI.Bridges.added_constraint_types(
     ::Type{<:BridgeType{T,F2,F3}},
 ) where {T,F2,F3}
     return Tuple{Type,Type}[(F2, S2), (F3, S3)]
@@ -74,8 +74,8 @@
 ) where {T,F2,F3}
     return MOI.supports(model, attr, MOI.ConstraintIndex{F2,S2}) ||
            MOI.supports(model, attr, MOI.ConstraintIndex{F3,S3})
-end
source
MathOptInterface.Bridges.Constraint.NormToPowerBridgeType
NormToPowerBridge{T,F} <: Bridges.Constraint.AbstractBridge

NormToPowerBridge implements the following reformulation:

  • $(t, x) \in NormCone(p, 1+d)$ into $(r_i, t, x_i) \in PowerCone(1 / p)$ for all $i$, and $\sum\limits_i r_i == t$.

For details, see Alizadeh, F., and Goldfarb, D. (2001). "Second-order cone programming." Mathematical Programming, Series B, 95:3-51.

Source node

NormToPowerBridge supports:

Target nodes

NormToPowerBridge creates:

source
MathOptInterface.Bridges.Constraint.NumberConversionBridgeType
NumberConversionBridge{T,F1,S1,F2,S2} <: Bridges.Constraint.AbstractBridge

NumberConversionBridge implements the following reformulation:

  • $f1(x) \in S1$ to $f2(x) \in S2$

where f and S are the same functional form, but differ in their coefficient type.

Source node

NumberConversionBridge supports:

  • F1 in S1

Target node

NumberConversionBridge creates:

  • F2 in S2
source
MathOptInterface.Bridges.Constraint.QuadtoSOCBridgeType
QuadtoSOCBridge{T} <: Bridges.Constraint.AbstractBridge

QuadtoSOCBridge converts quadratic inequalities

\[\frac{1}{2}x^T Q x + a^T x \le ub\]

into MOI.RotatedSecondOrderCone constraints, but it only applies when $Q$ is positive definite.

This is because, if Q is positive definite, there exists U such that $Q = U^T U$, and so the inequality can then be rewritten as;

\[\|U x\|_2^2 \le 2 (-a^T x + ub)\]

Therefore, QuadtoSOCBridge implements the following reformulations:

  • $\frac{1}{2}x^T Q x + a^T x \le ub$ into $(1, -a^T x + ub, Ux) \in RotatedSecondOrderCone$ where $Q = U^T U$
  • $\frac{1}{2}x^T Q x + a^T x \ge lb$ into $(1, a^T x - lb, Ux) \in RotatedSecondOrderCone$ where $-Q = U^T U$

Source node

QuadtoSOCBridge supports:

Target nodes

RelativeEntropyBridge creates:

Errors

This bridge errors if Q is not positive definite.

source
MathOptInterface.Bridges.Constraint.RSOCtoNonConvexQuadBridgeType
RSOCtoNonConvexQuadBridge{T} <: Bridges.Constraint.AbstractBridge

RSOCtoNonConvexQuadBridge implements the following reformulations:

  • $||x||_2^2 \le 2tu$ into $\sum x^2 - 2tu \le 0$, $1t + 0 \ge 0$, and $1u + 0 \ge 0$.

The MOI.ScalarAffineFunctions $1t + 0$ and $1u + 0$ are used in case the variables have other bound constraints.

Warning

This transformation starts from a convex constraint and creates a non-convex constraint. Unless the solver has explicit support for detecting rotated second-order cones in quadratic form, this may (wrongly) be interpreted by the solver as being non-convex. Therefore, this bridge is not added automatically by MOI.Bridges.full_bridge_optimizer. Care is recommended when adding this bridge to a optimizer.

Source node

RSOCtoNonConvexQuadBridge supports:

Target nodes

RSOCtoNonConvexQuadBridge creates:

source
MathOptInterface.Bridges.Constraint.ReifiedAllDifferentToCountDistinctBridgeType
ReifiedAllDifferentToCountDistinctBridge{T,F} <:
-Bridges.Constraint.AbstractBridge

ReifiedAllDifferentToCountDistinctBridge implements the following reformulations:

  • $r \iff x \in \textsf{AllDifferent}(d)$ to $r \iff (n, x) \in \textsf{CountDistinct}(1+d)$ and $n = d$
  • $r \iff f(x) \in \textsf{AllDifferent}(d)$ to $r \iff (d, f(x)) \in \textsf{CountDistinct}(1+d)$

Source node

ReifiedAllDifferentToCountDistinctBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

ReifiedAllDifferentToCountDistinctBridge creates:

source
MathOptInterface.Bridges.Constraint.ReifiedCountDistinctToMILPBridgeType
ReifiedCountDistinctToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

ReifiedCountDistinctToMILPBridge implements the following reformulation:

  • $r \iff (n, x) \in \textsf{CountDistinct}(1+d)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} +end

source
MathOptInterface.Bridges.Constraint.NormToPowerBridgeType
NormToPowerBridge{T,F} <: Bridges.Constraint.AbstractBridge

NormToPowerBridge implements the following reformulation:

  • $(t, x) \in NormCone(p, 1+d)$ into $(r_i, t, x_i) \in PowerCone(1 / p)$ for all $i$, and $\sum\limits_i r_i == t$.

For details, see Alizadeh, F., and Goldfarb, D. (2001). "Second-order cone programming." Mathematical Programming, Series B, 95:3-51.

Source node

NormToPowerBridge supports:

Target nodes

NormToPowerBridge creates:

source
MathOptInterface.Bridges.Constraint.NumberConversionBridgeType
NumberConversionBridge{T,F1,S1,F2,S2} <: Bridges.Constraint.AbstractBridge

NumberConversionBridge implements the following reformulation:

  • $f1(x) \in S1$ to $f2(x) \in S2$

where f and S are the same functional form, but differ in their coefficient type.

Source node

NumberConversionBridge supports:

  • F1 in S1

Target node

NumberConversionBridge creates:

  • F2 in S2
source
MathOptInterface.Bridges.Constraint.QuadtoSOCBridgeType
QuadtoSOCBridge{T} <: Bridges.Constraint.AbstractBridge

QuadtoSOCBridge converts quadratic inequalities

\[\frac{1}{2}x^T Q x + a^T x \le ub\]

into MOI.RotatedSecondOrderCone constraints, but it only applies when $Q$ is positive definite.

This is because, if Q is positive definite, there exists U such that $Q = U^T U$, and so the inequality can then be rewritten as;

\[\|U x\|_2^2 \le 2 (-a^T x + ub)\]

Therefore, QuadtoSOCBridge implements the following reformulations:

  • $\frac{1}{2}x^T Q x + a^T x \le ub$ into $(1, -a^T x + ub, Ux) \in RotatedSecondOrderCone$ where $Q = U^T U$
  • $\frac{1}{2}x^T Q x + a^T x \ge lb$ into $(1, a^T x - lb, Ux) \in RotatedSecondOrderCone$ where $-Q = U^T U$

Source node

QuadtoSOCBridge supports:

Target nodes

RelativeEntropyBridge creates:

Errors

This bridge errors if Q is not positive definite.

source
MathOptInterface.Bridges.Constraint.RSOCtoNonConvexQuadBridgeType
RSOCtoNonConvexQuadBridge{T} <: Bridges.Constraint.AbstractBridge

RSOCtoNonConvexQuadBridge implements the following reformulations:

  • $||x||_2^2 \le 2tu$ into $\sum x^2 - 2tu \le 0$, $1t + 0 \ge 0$, and $1u + 0 \ge 0$.

The MOI.ScalarAffineFunctions $1t + 0$ and $1u + 0$ are used in case the variables have other bound constraints.

Warning

This transformation starts from a convex constraint and creates a non-convex constraint. Unless the solver has explicit support for detecting rotated second-order cones in quadratic form, this may (wrongly) be interpreted by the solver as being non-convex. Therefore, this bridge is not added automatically by MOI.Bridges.full_bridge_optimizer. Care is recommended when adding this bridge to a optimizer.

Source node

RSOCtoNonConvexQuadBridge supports:

Target nodes

RSOCtoNonConvexQuadBridge creates:

source
MathOptInterface.Bridges.Constraint.ReifiedAllDifferentToCountDistinctBridgeType
ReifiedAllDifferentToCountDistinctBridge{T,F} <:
+Bridges.Constraint.AbstractBridge

ReifiedAllDifferentToCountDistinctBridge implements the following reformulations:

  • $r \iff x \in \textsf{AllDifferent}(d)$ to $r \iff (n, x) \in \textsf{CountDistinct}(1+d)$ and $n = d$
  • $r \iff f(x) \in \textsf{AllDifferent}(d)$ to $r \iff (d, f(x)) \in \textsf{CountDistinct}(1+d)$

Source node

ReifiedAllDifferentToCountDistinctBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

ReifiedAllDifferentToCountDistinctBridge creates:

source
MathOptInterface.Bridges.Constraint.ReifiedCountDistinctToMILPBridgeType
ReifiedCountDistinctToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

ReifiedCountDistinctToMILPBridge implements the following reformulation:

  • $r \iff (n, x) \in \textsf{CountDistinct}(1+d)$ into a mixed-integer linear program.

Reformulation

The reformulation is non-trivial, and it depends on the finite domain of each variable $x_i$, which we as define $S_i = \{l_i,\ldots,u_i\}$.

First, we introduce new binary variables $z_{ij}$, which are $1$ if variable $x_i$ takes the value $j$ in the optimal solution and $0$ otherwise:

\[\begin{aligned} z_{ij} \in \{0, 1\} & \;\; \forall i \in 1\ldots d, j \in S_i \\ x_i - \sum\limits_{j\in S_i} j \cdot z_{ij} = 0 & \;\; \forall i \in 1\ldots d \\ \sum\limits_{j\in S_i} z_{ij} = 1 & \;\; \forall i \in 1\ldots d \\ @@ -87,13 +87,13 @@ d_2 \le \delta^- \le M d_s \\ d_1 + d_2 + r = 1 \\ d_1, d_2 \in \{0, 1\} -\end{aligned}\]

Source node

ReifiedCountDistinctToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

ReifiedCountDistinctToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.RelativeEntropyBridgeType
RelativeEntropyBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

RelativeEntropyBridge implements the following reformulation that converts a MOI.RelativeEntropyCone into an MOI.ExponentialCone:

  • $u \ge \sum_{i=1}^n w_i \log \left(\frac{w_i}{v_i}\right)$ into $y_i \ge 0$, $u \ge \sum_{i=1}^n y_i$, and $(-y_i, w_i, v_i) \in ExponentialCone$.

Source node

RelativeEntropyBridge supports:

Target nodes

RelativeEntropyBridge creates:

source
MathOptInterface.Bridges.Constraint.RootDetBridgeType
RootDetBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

The MOI.RootDetConeTriangle is representable by MOI.PositiveSemidefiniteConeTriangle and MOI.GeometricMeanCone constraints, see [1, p. 149].

Indeed, $t \le \det(X)^{1/n}$ if and only if there exists a lower triangular matrix $Δ$ such that:

\[\begin{align*} +\end{aligned}\]

Source node

ReifiedCountDistinctToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

ReifiedCountDistinctToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.RelativeEntropyBridgeType
RelativeEntropyBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

RelativeEntropyBridge implements the following reformulation that converts a MOI.RelativeEntropyCone into an MOI.ExponentialCone:

  • $u \ge \sum_{i=1}^n w_i \log \left(\frac{w_i}{v_i}\right)$ into $y_i \ge 0$, $u \ge \sum_{i=1}^n y_i$, and $(-y_i, w_i, v_i) \in ExponentialCone$.

Source node

RelativeEntropyBridge supports:

Target nodes

RelativeEntropyBridge creates:

source
MathOptInterface.Bridges.Constraint.RootDetBridgeType
RootDetBridge{T,F,G,H} <: Bridges.Constraint.AbstractBridge

The MOI.RootDetConeTriangle is representable by MOI.PositiveSemidefiniteConeTriangle and MOI.GeometricMeanCone constraints, see [1, p. 149].

Indeed, $t \le \det(X)^{1/n}$ if and only if there exists a lower triangular matrix $Δ$ such that:

\[\begin{align*} \begin{pmatrix} X & Δ\\ Δ^\top & \mathrm{Diag}(Δ) \end{pmatrix} & \succeq 0\\ (t, \mathrm{Diag}(Δ)) & \in GeometricMeanCone -\end{align*}\]

Source node

RootDetBridge supports:

Target nodes

RootDetBridge creates:

[1] Ben-Tal, Aharon, and Arkadi Nemirovski. Lectures on modern convex optimization: analysis, algorithms, and engineering applications. Society for Industrial and Applied Mathematics, 2001.

source
MathOptInterface.Bridges.Constraint.SOCtoNonConvexQuadBridgeType
SOCtoNonConvexQuadBridge{T} <: Bridges.Constraint.AbstractBridge

SOCtoNonConvexQuadBridge implements the following reformulations:

  • $||x||_2 \le t$ into $\sum x^2 - t^2 \le 0$ and $1t + 0 \ge 0$

The MOI.ScalarAffineFunction $1t + 0$ is used in case the variable has other bound constraints.

Warning

This transformation starts from a convex constraint and creates a non-convex constraint. Unless the solver has explicit support for detecting second-order cones in quadratic form, this may (wrongly) be interpreted by the solver as being non-convex. Therefore, this bridge is not added automatically by MOI.Bridges.full_bridge_optimizer. Care is recommended when adding this bridge to a optimizer.

Source node

SOCtoNonConvexQuadBridge supports:

Target nodes

SOCtoNonConvexQuadBridge creates:

source
MathOptInterface.Bridges.Constraint.SOCtoPSDBridgeType
SOCtoPSDBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SOCtoPSDBridge implements the following reformulation:

  • $||x||_2 \le t$ into $\left[\begin{array}{c c}t & x^\top \\ x & t \mathbf{I}\end{array}\right]\succeq 0$
Warning

This bridge is not added by default by MOI.Bridges.full_bridge_optimizer because bridging second order cone constraints to semidefinite constraints can be achieved by the SOCtoRSOCBridge followed by the RSOCtoPSDBridge, while creating a smaller semidefinite constraint.

Source node

SOCtoPSDBridge supports:

Target nodes

SOCtoPSDBridge creates:

source
MathOptInterface.Bridges.Constraint.SOS1ToMILPBridgeType
SOS1ToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

SOS1ToMILPBridge implements the following reformulation:

  • $x \in \textsf{SOS1}(d)$ into a mixed-integer linear program.

Source node

SOS1ToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

SOS1ToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.SOS2ToMILPBridgeType
SOS2ToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

SOS2ToMILPBridge implements the following reformulation:

  • $x \in \textsf{SOS2}(d)$ into a mixed-integer linear program.

Source node

SOS2ToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

SOS2ToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ScalarizeBridgeType
ScalarizeBridge{T,F,S}

ScalarizeBridge implements the following reformulations:

  • $f(x) - a \in \mathbb{R}_+$ into $f_i(x) \ge a_i$ for all $i$
  • $f(x) - a \in \mathbb{R}_-$ into $f_i(x) \le a_i$ for all $i$
  • $f(x) - a \in \{0\}$ into $f_i(x) == a_i$ for all $i$

Source node

ScalarizeBridge supports:

Target nodes

ScalarizeBridge creates:

source
MathOptInterface.Bridges.Constraint.SemiToBinaryBridgeType
SemiToBinaryBridge{T,S} <: Bridges.Constraint.AbstractBridge

SemiToBinaryBridge implements the following reformulations:

  • $x \in \{0\} \cup [l, u]$ into

    \[\begin{aligned} +\end{align*}\]

    Source node

    RootDetBridge supports:

    Target nodes

    RootDetBridge creates:

    [1] Ben-Tal, Aharon, and Arkadi Nemirovski. Lectures on modern convex optimization: analysis, algorithms, and engineering applications. Society for Industrial and Applied Mathematics, 2001.

source
MathOptInterface.Bridges.Constraint.SOCtoNonConvexQuadBridgeType
SOCtoNonConvexQuadBridge{T} <: Bridges.Constraint.AbstractBridge

SOCtoNonConvexQuadBridge implements the following reformulations:

  • $||x||_2 \le t$ into $\sum x^2 - t^2 \le 0$ and $1t + 0 \ge 0$

The MOI.ScalarAffineFunction $1t + 0$ is used in case the variable has other bound constraints.

Warning

This transformation starts from a convex constraint and creates a non-convex constraint. Unless the solver has explicit support for detecting second-order cones in quadratic form, this may (wrongly) be interpreted by the solver as being non-convex. Therefore, this bridge is not added automatically by MOI.Bridges.full_bridge_optimizer. Care is recommended when adding this bridge to a optimizer.

Source node

SOCtoNonConvexQuadBridge supports:

Target nodes

SOCtoNonConvexQuadBridge creates:

source
MathOptInterface.Bridges.Constraint.SOCtoPSDBridgeType
SOCtoPSDBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SOCtoPSDBridge implements the following reformulation:

  • $||x||_2 \le t$ into $\left[\begin{array}{c c}t & x^\top \\ x & t \mathbf{I}\end{array}\right]\succeq 0$
Warning

This bridge is not added by default by MOI.Bridges.full_bridge_optimizer because bridging second order cone constraints to semidefinite constraints can be achieved by the SOCtoRSOCBridge followed by the RSOCtoPSDBridge, while creating a smaller semidefinite constraint.

Source node

SOCtoPSDBridge supports:

Target nodes

SOCtoPSDBridge creates:

source
MathOptInterface.Bridges.Constraint.SOS1ToMILPBridgeType
SOS1ToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

SOS1ToMILPBridge implements the following reformulation:

  • $x \in \textsf{SOS1}(d)$ into a mixed-integer linear program.

Source node

SOS1ToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

SOS1ToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.SOS2ToMILPBridgeType
SOS2ToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

SOS2ToMILPBridge implements the following reformulation:

  • $x \in \textsf{SOS2}(d)$ into a mixed-integer linear program.

Source node

SOS2ToMILPBridge supports:

where F is MOI.VectorOfVariables or MOI.VectorAffineFunction{T}.

Target nodes

SOS2ToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.ScalarizeBridgeType
ScalarizeBridge{T,F,S}

ScalarizeBridge implements the following reformulations:

  • $f(x) - a \in \mathbb{R}_+$ into $f_i(x) \ge a_i$ for all $i$
  • $f(x) - a \in \mathbb{R}_-$ into $f_i(x) \le a_i$ for all $i$
  • $f(x) - a \in \{0\}$ into $f_i(x) == a_i$ for all $i$

Source node

ScalarizeBridge supports:

Target nodes

ScalarizeBridge creates:

source
MathOptInterface.Bridges.Constraint.SemiToBinaryBridgeType
SemiToBinaryBridge{T,S} <: Bridges.Constraint.AbstractBridge

SemiToBinaryBridge implements the following reformulations:

  • $x \in \{0\} \cup [l, u]$ into

    \[\begin{aligned} x \leq z u \\ x \geq z l \\ z \in \{0, 1\} @@ -102,8 +102,8 @@ x \geq z l \\ z \in \{0, 1\} \\ x \in \mathbb{Z} -\end{aligned}\]

Source node

SemiToBinaryBridge supports:

Target nodes

SemiToBinaryBridge creates:

source
MathOptInterface.Bridges.Constraint.SetConversionBridgeType
SetConversionBridge{T,S2,S1,F} <:
-    MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F}

SetConversionBridge implements the following reformulations:

  • $f(x) \in S1$ into $f(x) \in S2$

In order to add this bridge, you need to create a bridge specific for a given type T and set S2:

MOI.Bridges.add_bridge(model, MOI.Bridges.Constraint.SetConversionBridge{T,S2})

In order to define a bridge with S2 specified but T unspecified, for example for JuMP.add_bridge, you can use

const MyBridge{T,S1,F} = MOI.Bridges.Constraint.SetConversionBridge{T,S2,S1,F}

See also FunctionConversionBridge.

Source node

SetConversionBridge supports:

  • F in S1

Target nodes

SetConversionBridge creates:

  • F in S2
source
MathOptInterface.Bridges.Constraint.SetMapBridgeType
abstract type SetMapBridge{T,S2,S1,F,G} <: MultiSetMapBridge{T,S1,G} end

Consider two type of sets, S1 and S2, and a linear mapping A such that the image of a set of type S1 under A is a set of type S2.

A SetMapBridge{T,S2,S1,F,G} is a bridge that maps G-in-S1 constraints into F-in-S2 by mapping the function through A.

The linear map A is described by;

Implementing a method for these two functions is sufficient to bridge constraints. However, in order for the getters and setters of attributes such as dual solutions and starting values to work as well, a method for the following functions must be implemented:

See the docstrings of each function to see which feature would be missing if it was not implemented for a given bridge.

source
MathOptInterface.Bridges.Constraint.SplitComplexEqualToBridgeType
SplitComplexEqualToBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SplitComplexEqualToBridge implements the following reformulation:

  • $f(x) + g(x) * im = a + b * im$ into $f(x) = a$ and $g(x) = b$

Source node

SplitComplexEqualToBridge supports:

where G is a function with Complex coefficients.

Target nodes

SplitComplexEqualToBridge creates:

where F is the type of the real/imaginary part of G.

source
MathOptInterface.Bridges.Constraint.SplitComplexZerosBridgeType
SplitComplexZerosBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SplitComplexZerosBridge implements the following reformulation:

  • $f(x) \in \{0\}^n$ into $\text{Re}(f(x)) \in \{0\}^n$ and $\text{Im}(f(x)) \in \{0\}^n$

Source node

SplitComplexZerosBridge supports:

where G is a function with Complex coefficients.

Target nodes

SplitComplexZerosBridge creates:

where F is the type of the real/imaginary part of G.

source
MathOptInterface.Bridges.Constraint.SplitIntervalBridgeType
SplitIntervalBridge{T,F,S,LS,US} <: Bridges.Constraint.AbstractBridge

SplitIntervalBridge implements the following reformulations:

  • $l \le f(x) \le u$ into $f(x) \ge l$ and $f(x) \le u$
  • $f(x) = b$ into $f(x) \ge b$ and $f(x) \le b$
  • $f(x) \in \{0\}$ into $f(x) \in \mathbb{R}_+$ and $f(x) \in \mathbb{R}_-$

Source node

SplitIntervalBridge supports:

Target nodes

SplitIntervalBridge creates:

or

Note

If T<:AbstractFloat and S is MOI.Interval{T} then no lower (resp. upper) bound constraint is created if the lower (resp. upper) bound is typemin(T) (resp. typemax(T)). Similarly, when MOI.ConstraintSet is set, a lower or upper bound constraint may be deleted or created accordingly.

source
MathOptInterface.Bridges.Constraint.SquareBridgeType
SquareBridge{T,F,G,TT,ST} <: Bridges.Constraint.AbstractBridge

SquareBridge implements the following reformulations:

  • $(t, u, X) \in LogDetConeSquare$ into $(t, u, Y) in LogDetConeTriangle$
  • $(t, X) \in RootDetConeSquare$ into $(t, Y) in RootDetConeTriangle$
  • $X \in AbstractSymmetricMatrixSetSquare$ into $Y in AbstractSymmetricMatrixSetTriangle$

where $Y$ is the upper triangluar component of $X$.

In addition, constraints are added as necessary to constrain the matrix $X$ to be symmetric. For example, the constraint for the matrix:

\[\begin{pmatrix} +\end{aligned}\]

Source node

SemiToBinaryBridge supports:

Target nodes

SemiToBinaryBridge creates:

source
MathOptInterface.Bridges.Constraint.SetConversionBridgeType
SetConversionBridge{T,S2,S1,F} <:
+    MOI.Bridges.Constraint.SetMapBridge{T,S2,S1,F,F}

SetConversionBridge implements the following reformulations:

  • $f(x) \in S1$ into $f(x) \in S2$

In order to add this bridge, you need to create a bridge specific for a given type T and set S2:

MOI.Bridges.add_bridge(model, MOI.Bridges.Constraint.SetConversionBridge{T,S2})

In order to define a bridge with S2 specified but T unspecified, for example for JuMP.add_bridge, you can use

const MyBridge{T,S1,F} = MOI.Bridges.Constraint.SetConversionBridge{T,S2,S1,F}

See also FunctionConversionBridge.

Source node

SetConversionBridge supports:

  • F in S1

Target nodes

SetConversionBridge creates:

  • F in S2
source
MathOptInterface.Bridges.Constraint.SetMapBridgeType
abstract type SetMapBridge{T,S2,S1,F,G} <: MultiSetMapBridge{T,S1,G} end

Consider two type of sets, S1 and S2, and a linear mapping A such that the image of a set of type S1 under A is a set of type S2.

A SetMapBridge{T,S2,S1,F,G} is a bridge that maps G-in-S1 constraints into F-in-S2 by mapping the function through A.

The linear map A is described by;

Implementing a method for these two functions is sufficient to bridge constraints. However, in order for the getters and setters of attributes such as dual solutions and starting values to work as well, a method for the following functions must be implemented:

See the docstrings of each function to see which feature would be missing if it was not implemented for a given bridge.

source
MathOptInterface.Bridges.Constraint.SplitComplexEqualToBridgeType
SplitComplexEqualToBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SplitComplexEqualToBridge implements the following reformulation:

  • $f(x) + g(x) * im = a + b * im$ into $f(x) = a$ and $g(x) = b$

Source node

SplitComplexEqualToBridge supports:

where G is a function with Complex coefficients.

Target nodes

SplitComplexEqualToBridge creates:

where F is the type of the real/imaginary part of G.

source
MathOptInterface.Bridges.Constraint.SplitComplexZerosBridgeType
SplitComplexZerosBridge{T,F,G} <: Bridges.Constraint.AbstractBridge

SplitComplexZerosBridge implements the following reformulation:

  • $f(x) \in \{0\}^n$ into $\text{Re}(f(x)) \in \{0\}^n$ and $\text{Im}(f(x)) \in \{0\}^n$

Source node

SplitComplexZerosBridge supports:

where G is a function with Complex coefficients.

Target nodes

SplitComplexZerosBridge creates:

where F is the type of the real/imaginary part of G.

source
MathOptInterface.Bridges.Constraint.SplitIntervalBridgeType
SplitIntervalBridge{T,F,S,LS,US} <: Bridges.Constraint.AbstractBridge

SplitIntervalBridge implements the following reformulations:

  • $l \le f(x) \le u$ into $f(x) \ge l$ and $f(x) \le u$
  • $f(x) = b$ into $f(x) \ge b$ and $f(x) \le b$
  • $f(x) \in \{0\}$ into $f(x) \in \mathbb{R}_+$ and $f(x) \in \mathbb{R}_-$

Source node

SplitIntervalBridge supports:

Target nodes

SplitIntervalBridge creates:

or

Note

If T<:AbstractFloat and S is MOI.Interval{T} then no lower (resp. upper) bound constraint is created if the lower (resp. upper) bound is typemin(T) (resp. typemax(T)). Similarly, when MOI.ConstraintSet is set, a lower or upper bound constraint may be deleted or created accordingly.

source
MathOptInterface.Bridges.Constraint.SquareBridgeType
SquareBridge{T,F,G,TT,ST} <: Bridges.Constraint.AbstractBridge

SquareBridge implements the following reformulations:

  • $(t, u, X) \in LogDetConeSquare$ into $(t, u, Y) in LogDetConeTriangle$
  • $(t, X) \in RootDetConeSquare$ into $(t, Y) in RootDetConeTriangle$
  • $X \in AbstractSymmetricMatrixSetSquare$ into $Y in AbstractSymmetricMatrixSetTriangle$

where $Y$ is the upper triangluar component of $X$.

In addition, constraints are added as necessary to constrain the matrix $X$ to be symmetric. For example, the constraint for the matrix:

\[\begin{pmatrix} 1 & 1 + x & 2 - 3x\\ 1 + x & 2 + x & 3 - x\\ 2 - 3x & 2 + x & 2x @@ -111,11 +111,11 @@ 1 & 1 + x & 2 - 3x\\ \cdot & 2 + x & 3 - x\\ \cdot & \cdot & 2x -\end{pmatrix}\]

and the equality constraint between the off-diagonal entries (2, 3) and (3, 2) $3 - x == 2 + x$. Note that no symmetrization constraint needs to be added between the off-diagonal entries (1, 2) and (2, 1) or between (1, 3) and (3, 1) because the expressions are the same.

Source node

SquareBridge supports:

  • F in ST

Target nodes

SquareBridge creates:

  • G in TT
source
MathOptInterface.Bridges.Constraint.TableToMILPBridgeType
TableToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

TableToMILPBridge implements the following reformulation:

  • $x \in Table(t)$ into

    \[\begin{aligned} +\end{pmatrix}\]

    and the equality constraint between the off-diagonal entries (2, 3) and (3, 2) $3 - x == 2 + x$. Note that no symmetrization constraint needs to be added between the off-diagonal entries (1, 2) and (2, 1) or between (1, 3) and (3, 1) because the expressions are the same.

    Source node

    SquareBridge supports:

    • F in ST

    Target nodes

    SquareBridge creates:

    • G in TT
source
MathOptInterface.Bridges.Constraint.TableToMILPBridgeType
TableToMILPBridge{T,F} <: Bridges.Constraint.AbstractBridge

TableToMILPBridge implements the following reformulation:

  • $x \in Table(t)$ into

    \[\begin{aligned} z_{j} \in \{0, 1\} & \quad \forall i, j \\ \sum\limits_{j=1}^n z_{j} = 1 \\ \sum\limits_{j=1}^n t_{ij} z_{j} = x_i & \quad \forall i -\end{aligned}\]

Source node

TableToMILPBridge supports:

Target nodes

TableToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.VectorizeBridgeType
VectorizeBridge{T,F,S,G} <: Bridges.Constraint.AbstractBridge

VectorizeBridge implements the following reformulations:

  • $g(x) \ge a$ into $[g(x) - a] \in \mathbb{R}_+$
  • $g(x) \le a$ into $[g(x) - a] \in \mathbb{R}_-$
  • $g(x) == a$ into $[g(x) - a] \in \{0\}$

where T is the coefficient type of g(x) - a.

Source node

VectorizeBridge supports:

Target nodes

VectorizeBridge creates:

source
MathOptInterface.Bridges.Constraint.ZeroOneBridgeType
ZeroOneBridge{T} <: Bridges.Constraint.AbstractBridge

ZeroOneBridge implements the following reformulation:

  • $x \in \{0, 1\}$ into $x \in \mathbb{Z}$, $1x \in [0, 1]$.
Note

ZeroOneBridge adds a linear constraint instead of adding variable bounds to avoid conflicting with bounds set by the user.

Source node

ZeroOneBridge supports:

Target nodes

ZeroOneBridge creates:

source

Objective bridges

These bridges are subtypes of Bridges.Objective.AbstractBridge.

MathOptInterface.Bridges.Objective.FunctionConversionBridgeType
FunctionConversionBridge{T,F,G} <: AbstractBridge

FunctionConversionBridge implements the following reformulations:

  • $\min \{g(x)\}$ into $\min\{f(x)\}$
  • $\max \{g(x)\}$ into $\max\{f(x)\}$

for these pairs of functions:

Source node

FunctionConversionBridge supports:

Target nodes

FunctionConversionBridge creates:

source
MathOptInterface.Bridges.Objective.QuadratizeBridgeType
QuadratizeBridge{T,G} <: FunctionConversionBridge{T,MOI.ScalarQuadraticFunction{T},G}

QuadratizeBridge implements the following reformulations:

  • $\min \{a^\top x + b\}$ into $\min\{x^\top \mathbf{0} x + a^\top x + b\}$
  • $\max \{a^\top x + b\}$ into $\max\{x^\top \mathbf{0} x + a^\top x + b\}$

where T is the coefficient type of 0.

Source node

QuadratizeBridge supports:

Target nodes

QuadratizeBridge creates:

source
MathOptInterface.Bridges.Objective.SlackBridgeType
SlackBridge{T,F,G}

SlackBridge implements the following reformulations:

  • $\min\{f(x)\}$ into $\min\{y\;|\; f(x) - y \le 0\}$
  • $\max\{f(x)\}$ into $\max\{y\;|\; f(x) - y \ge 0\}$

where F is the type of f(x) - y, G is the type of f(x), and T is the coefficient type of f(x).

Source node

SlackBridge supports:

Target nodes

SlackBridge creates:

Warning

When using this bridge, changing the optimization sense is not supported. Set the sense to MOI.FEASIBILITY_SENSE first to delete the bridge, then set MOI.ObjectiveSense and re-add the objective.

source
MathOptInterface.Bridges.Objective.VectorFunctionizeBridgeType
VectorFunctionizeBridge{T,G} <: FunctionConversionBridge{T,MOI.VectorAffineFunction{T},G}

VectorFunctionizeBridge implements the following reformulations:

  • $\min \{x\}$ into $\min\{1x + 0\}$
  • $\max \{x\}$ into $\max\{1x + 0\}$

where T is the coefficient type of 1 and 0.

Source node

VectorFunctionizeBridge supports:

Target nodes

VectorFunctionizeBridge creates:

source
MathOptInterface.Bridges.Objective.VectorSlackBridgeType
VectorSlackBridge{T,F,G}

VectorSlackBridge implements the following reformulations:

  • $\min\{f(x)\}$ into $\min\{y\;|\; y - f(x) \in \mathbb{R}_+ \}$
  • $\max\{f(x)\}$ into $\max\{y\;|\; f(x) - y \in \mathbb{R}_+ \}$

where F is the type of f(x) - y, G is the type of f(x), and T is the coefficient type of f(x).

Source node

VectorSlackBridge supports:

Target nodes

VectorSlackBridge creates:

Warning

When using this bridge, changing the optimization sense is not supported. Set the sense to MOI.FEASIBILITY_SENSE first to delete the bridge, then set MOI.ObjectiveSense and re-add the objective.

source

Variable bridges

These bridges are subtypes of Bridges.Variable.AbstractBridge.

MathOptInterface.Bridges.Variable.HermitianToSymmetricPSDBridgeType
HermitianToSymmetricPSDBridge{T} <: Bridges.Variable.AbstractBridge

HermitianToSymmetricPSDBridge implements the following reformulation:

  • Hermitian positive semidefinite n x n complex matrix to a symmetric positive semidefinite 2n x 2n real matrix satisfying equality constraints described below.

Source node

HermitianToSymmetricPSDBridge supports:

Target node

HermitianToSymmetricPSDBridge creates:

Reformulation

The reformulation is best described by example.

The Hermitian matrix:

\[\begin{bmatrix} +\end{aligned}\]

Source node

TableToMILPBridge supports:

Target nodes

TableToMILPBridge creates:

source
MathOptInterface.Bridges.Constraint.VectorizeBridgeType
VectorizeBridge{T,F,S,G} <: Bridges.Constraint.AbstractBridge

VectorizeBridge implements the following reformulations:

  • $g(x) \ge a$ into $[g(x) - a] \in \mathbb{R}_+$
  • $g(x) \le a$ into $[g(x) - a] \in \mathbb{R}_-$
  • $g(x) == a$ into $[g(x) - a] \in \{0\}$

where T is the coefficient type of g(x) - a.

Source node

VectorizeBridge supports:

Target nodes

VectorizeBridge creates:

source
MathOptInterface.Bridges.Constraint.ZeroOneBridgeType
ZeroOneBridge{T} <: Bridges.Constraint.AbstractBridge

ZeroOneBridge implements the following reformulation:

  • $x \in \{0, 1\}$ into $x \in \mathbb{Z}$, $1x \in [0, 1]$.
Note

ZeroOneBridge adds a linear constraint instead of adding variable bounds to avoid conflicting with bounds set by the user.

Source node

ZeroOneBridge supports:

Target nodes

ZeroOneBridge creates:

source

Objective bridges

These bridges are subtypes of Bridges.Objective.AbstractBridge.

MathOptInterface.Bridges.Objective.FunctionConversionBridgeType
FunctionConversionBridge{T,F,G} <: AbstractBridge

FunctionConversionBridge implements the following reformulations:

  • $\min \{g(x)\}$ into $\min\{f(x)\}$
  • $\max \{g(x)\}$ into $\max\{f(x)\}$

for these pairs of functions:

Source node

FunctionConversionBridge supports:

Target nodes

FunctionConversionBridge creates:

source
MathOptInterface.Bridges.Objective.QuadratizeBridgeType
QuadratizeBridge{T,G} <: FunctionConversionBridge{T,MOI.ScalarQuadraticFunction{T},G}

QuadratizeBridge implements the following reformulations:

  • $\min \{a^\top x + b\}$ into $\min\{x^\top \mathbf{0} x + a^\top x + b\}$
  • $\max \{a^\top x + b\}$ into $\max\{x^\top \mathbf{0} x + a^\top x + b\}$

where T is the coefficient type of 0.

Source node

QuadratizeBridge supports:

Target nodes

QuadratizeBridge creates:

source
MathOptInterface.Bridges.Objective.SlackBridgeType
SlackBridge{T,F,G}

SlackBridge implements the following reformulations:

  • $\min\{f(x)\}$ into $\min\{y\;|\; f(x) - y \le 0\}$
  • $\max\{f(x)\}$ into $\max\{y\;|\; f(x) - y \ge 0\}$

where F is the type of f(x) - y, G is the type of f(x), and T is the coefficient type of f(x).

Source node

SlackBridge supports:

Target nodes

SlackBridge creates:

Warning

When using this bridge, changing the optimization sense is not supported. Set the sense to MOI.FEASIBILITY_SENSE first to delete the bridge, then set MOI.ObjectiveSense and re-add the objective.

source
MathOptInterface.Bridges.Objective.VectorFunctionizeBridgeType
VectorFunctionizeBridge{T,G} <: FunctionConversionBridge{T,MOI.VectorAffineFunction{T},G}

VectorFunctionizeBridge implements the following reformulations:

  • $\min \{x\}$ into $\min\{1x + 0\}$
  • $\max \{x\}$ into $\max\{1x + 0\}$

where T is the coefficient type of 1 and 0.

Source node

VectorFunctionizeBridge supports:

Target nodes

VectorFunctionizeBridge creates:

source
MathOptInterface.Bridges.Objective.VectorSlackBridgeType
VectorSlackBridge{T,F,G}

VectorSlackBridge implements the following reformulations:

  • $\min\{f(x)\}$ into $\min\{y\;|\; y - f(x) \in \mathbb{R}_+ \}$
  • $\max\{f(x)\}$ into $\max\{y\;|\; f(x) - y \in \mathbb{R}_+ \}$

where F is the type of f(x) - y, G is the type of f(x), and T is the coefficient type of f(x).

Source node

VectorSlackBridge supports:

Target nodes

VectorSlackBridge creates:

Warning

When using this bridge, changing the optimization sense is not supported. Set the sense to MOI.FEASIBILITY_SENSE first to delete the bridge, then set MOI.ObjectiveSense and re-add the objective.

source

Variable bridges

These bridges are subtypes of Bridges.Variable.AbstractBridge.

MathOptInterface.Bridges.Variable.HermitianToSymmetricPSDBridgeType
HermitianToSymmetricPSDBridge{T} <: Bridges.Variable.AbstractBridge

HermitianToSymmetricPSDBridge implements the following reformulation:

  • Hermitian positive semidefinite n x n complex matrix to a symmetric positive semidefinite 2n x 2n real matrix satisfying equality constraints described below.

Source node

HermitianToSymmetricPSDBridge supports:

Target node

HermitianToSymmetricPSDBridge creates:

Reformulation

The reformulation is best described by example.

The Hermitian matrix:

\[\begin{bmatrix} x_{11} & x_{12} + y_{12}im & x_{13} + y_{13}im\\ x_{12} - y_{12}im & x_{22} & x_{23} + y_{23}im\\ x_{13} - y_{13}im & x_{23} - y_{23}im & x_{33} @@ -126,4 +126,4 @@ & & & x_{11} & x_{12} & x_{13} \\ & & & & x_{22} & x_{23} \\ & & & & & x_{33} -\end{bmatrix}\]

is positive semidefinite.

The bridge achieves this reformulation by adding a new set of variables in MOI.PositiveSemidefiniteConeTriangle(6), and then adding three groups of equality constraints to:

  • constrain the two x blocks to be equal
  • force the diagonal of the y blocks to be 0
  • force the lower triangular of the y block to be the negative of the upper triangle.
source
MathOptInterface.Bridges.Variable.RSOCtoPSDBridgeType
RSOCtoPSDBridge{T} <: Bridges.Variable.AbstractBridge

RSOCtoPSDBridge implements the following reformulation:

  • $||x||_2^2 \le 2tu$ where $t, u \ge 0$ into $Y \succeq 0$, with the substitution rule: $Y = \left[\begin{array}{c c}t & x^\top \\ x & 2u \mathbf{I}\end{array}\right].$

Additional bounds are added to ensure the off-diagonals of the $2uI$ submatrix are 0, and linear constraints are added to ensure the diagonal of $2uI$ takes the same values.

As a special case, if $|x|| = 0$, then RSOCtoPSDBridge reformulates into $(t, u) \in \mathbb{R}_+$.

Source node

RSOCtoPSDBridge supports:

Target nodes

RSOCtoPSDBridge creates:

source
MathOptInterface.Bridges.Variable.RSOCtoSOCBridgeType
RSOCtoSOCBridge{T} <: Bridges.Variable.AbstractBridge

RSOCtoSOCBridge implements the following reformulation:

  • $||x||_2^2 \le 2tu$ into $||v||_2 \le w$, with the substitution rules $t = \frac{w}{\sqrt 2} + \frac{v_1}{\sqrt 2}$, $u = \frac{w}{\sqrt 2} - \frac{v_1}{\sqrt 2}$, and $x = (v_2,\ldots,v_N)$.

Source node

RSOCtoSOCBridge supports:

Target node

RSOCtoSOCBridge creates:

source
MathOptInterface.Bridges.Variable.SOCtoRSOCBridgeType
SOCtoRSOCBridge{T} <: Bridges.Variable.AbstractBridge

SOCtoRSOCBridge implements the following reformulation:

  • $||x||_2 \le t$ into $2uv \ge ||w||_2^2$, with the substitution rules $t = \frac{u}{\sqrt 2} + \frac{v}{\sqrt 2}$, $x = (\frac{u}{\sqrt 2} - \frac{v}{\sqrt 2}, w)$.

Assumptions

  • SOCtoRSOCBridge assumes that $|x| \ge 1$.

Source node

SOCtoRSOCBridge supports:

Target node

SOCtoRSOCBridge creates:

source
MathOptInterface.Bridges.Variable.SetMapBridgeType
abstract type SetMapBridge{T,S1,S2} <: AbstractBridge end

Consider two type of sets, S1 and S2, and a linear mapping A such that the image of a set of type S1 under A is a set of type S2.

A SetMapBridge{T,S1,S2} is a bridge that substitutes constrained variables in S2 into the image through A of constrained variables in S1.

The linear map A is described by:

Implementing a method for these two functions is sufficient to bridge constrained variables. However, in order for the getters and setters of attributes such as dual solutions and starting values to work as well, a method for the following functions must be implemented:

See the docstrings of each function to see which feature would be missing if it was not implemented for a given bridge.

source
MathOptInterface.Bridges.Variable.VectorizeBridgeType
VectorizeBridge{T,S} <: Bridges.Variable.AbstractBridge

VectorizeBridge implements the following reformulations:

  • $x \ge a$ into $[y] \in \mathbb{R}_+$ with the substitution rule $x = a + y$
  • $x \le a$ into $[y] \in \mathbb{R}_-$ with the substitution rule $x = a + y$
  • $x == a$ into $[y] \in \{0\}$ with the substitution rule $x = a + y$

where T is the coefficient type of a + y.

Source node

VectorizeBridge supports:

Target nodes

VectorizeBridge creates:

source
MathOptInterface.Bridges.Variable.ZerosBridgeType
ZerosBridge{T} <: Bridges.Variable.AbstractBridge

ZerosBridge implements the following reformulation:

  • $x \in \{0\}$ into the substitution rule $x = 0$,

where T is the coefficient type of 0.

Source node

ZerosBridge supports:

Target nodes

ZerosBridge does not create target nodes. It replaces all instances of x with 0 via substitution. This means that no variables are created in the underlying model.

Caveats

The bridged variables are similar to parameters with zero values. Parameters with non-zero values can be created with constrained variables in MOI.EqualTo by combining a VectorizeBridge and this bridge.

However, functions modified by ZerosBridge cannot be unbridged. That is, for a given function, we cannot determine if the bridged variables were used.

A related implication is that this bridge does not support MOI.ConstraintDual. However, if a MOI.Utilities.CachingOptimizer is used, the dual can be determined by the bridged optimizer using MOI.Utilities.get_fallback because the caching optimizer records the unbridged function.

source
+\end{bmatrix}\]

is positive semidefinite.

The bridge achieves this reformulation by adding a new set of variables in MOI.PositiveSemidefiniteConeTriangle(6), and then adding three groups of equality constraints to:

  • constrain the two x blocks to be equal
  • force the diagonal of the y blocks to be 0
  • force the lower triangular of the y block to be the negative of the upper triangle.
source
MathOptInterface.Bridges.Variable.RSOCtoPSDBridgeType
RSOCtoPSDBridge{T} <: Bridges.Variable.AbstractBridge

RSOCtoPSDBridge implements the following reformulation:

  • $||x||_2^2 \le 2tu$ where $t, u \ge 0$ into $Y \succeq 0$, with the substitution rule: $Y = \left[\begin{array}{c c}t & x^\top \\ x & 2u \mathbf{I}\end{array}\right].$

Additional bounds are added to ensure the off-diagonals of the $2uI$ submatrix are 0, and linear constraints are added to ensure the diagonal of $2uI$ takes the same values.

As a special case, if $|x|| = 0$, then RSOCtoPSDBridge reformulates into $(t, u) \in \mathbb{R}_+$.

Source node

RSOCtoPSDBridge supports:

Target nodes

RSOCtoPSDBridge creates:

source
MathOptInterface.Bridges.Variable.RSOCtoSOCBridgeType
RSOCtoSOCBridge{T} <: Bridges.Variable.AbstractBridge

RSOCtoSOCBridge implements the following reformulation:

  • $||x||_2^2 \le 2tu$ into $||v||_2 \le w$, with the substitution rules $t = \frac{w}{\sqrt 2} + \frac{v_1}{\sqrt 2}$, $u = \frac{w}{\sqrt 2} - \frac{v_1}{\sqrt 2}$, and $x = (v_2,\ldots,v_N)$.

Source node

RSOCtoSOCBridge supports:

Target node

RSOCtoSOCBridge creates:

source
MathOptInterface.Bridges.Variable.SOCtoRSOCBridgeType
SOCtoRSOCBridge{T} <: Bridges.Variable.AbstractBridge

SOCtoRSOCBridge implements the following reformulation:

  • $||x||_2 \le t$ into $2uv \ge ||w||_2^2$, with the substitution rules $t = \frac{u}{\sqrt 2} + \frac{v}{\sqrt 2}$, $x = (\frac{u}{\sqrt 2} - \frac{v}{\sqrt 2}, w)$.

Assumptions

  • SOCtoRSOCBridge assumes that $|x| \ge 1$.

Source node

SOCtoRSOCBridge supports:

Target node

SOCtoRSOCBridge creates:

source
MathOptInterface.Bridges.Variable.SetMapBridgeType
abstract type SetMapBridge{T,S1,S2} <: AbstractBridge end

Consider two type of sets, S1 and S2, and a linear mapping A such that the image of a set of type S1 under A is a set of type S2.

A SetMapBridge{T,S1,S2} is a bridge that substitutes constrained variables in S2 into the image through A of constrained variables in S1.

The linear map A is described by:

Implementing a method for these two functions is sufficient to bridge constrained variables. However, in order for the getters and setters of attributes such as dual solutions and starting values to work as well, a method for the following functions must be implemented:

See the docstrings of each function to see which feature would be missing if it was not implemented for a given bridge.

source
MathOptInterface.Bridges.Variable.VectorizeBridgeType
VectorizeBridge{T,S} <: Bridges.Variable.AbstractBridge

VectorizeBridge implements the following reformulations:

  • $x \ge a$ into $[y] \in \mathbb{R}_+$ with the substitution rule $x = a + y$
  • $x \le a$ into $[y] \in \mathbb{R}_-$ with the substitution rule $x = a + y$
  • $x == a$ into $[y] \in \{0\}$ with the substitution rule $x = a + y$

where T is the coefficient type of a + y.

Source node

VectorizeBridge supports:

Target nodes

VectorizeBridge creates:

source
MathOptInterface.Bridges.Variable.ZerosBridgeType
ZerosBridge{T} <: Bridges.Variable.AbstractBridge

ZerosBridge implements the following reformulation:

  • $x \in \{0\}$ into the substitution rule $x = 0$,

where T is the coefficient type of 0.

Source node

ZerosBridge supports:

Target nodes

ZerosBridge does not create target nodes. It replaces all instances of x with 0 via substitution. This means that no variables are created in the underlying model.

Caveats

The bridged variables are similar to parameters with zero values. Parameters with non-zero values can be created with constrained variables in MOI.EqualTo by combining a VectorizeBridge and this bridge.

However, functions modified by ZerosBridge cannot be unbridged. That is, for a given function, we cannot determine if the bridged variables were used.

A related implication is that this bridge does not support MOI.ConstraintDual. However, if a MOI.Utilities.CachingOptimizer is used, the dual can be determined by the bridged optimizer using MOI.Utilities.get_fallback because the caching optimizer records the unbridged function.

source
diff --git a/dev/submodules/Bridges/overview/index.html b/dev/submodules/Bridges/overview/index.html index 394f596993..f95d75e968 100644 --- a/dev/submodules/Bridges/overview/index.html +++ b/dev/submodules/Bridges/overview/index.html @@ -62,4 +62,4 @@ julia> MOI.get(inner_optimizer, MOI.ListOfConstraintTypesPresent()) 1-element Vector{Tuple{Type, Type}}: - (MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}) + (MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}) diff --git a/dev/submodules/Bridges/reference/index.html b/dev/submodules/Bridges/reference/index.html index 25674f7bb5..6298a1b207 100644 --- a/dev/submodules/Bridges/reference/index.html +++ b/dev/submodules/Bridges/reference/index.html @@ -1,22 +1,22 @@ -API Reference · MathOptInterface

Bridges

AbstractBridge API

MathOptInterface.Bridges.AbstractBridgeType
abstract type AbstractBridge end

An abstract type representing a bridged constraint or variable in a MOI.Bridges.AbstractBridgeOptimizer.

All bridges must implement:

Subtypes of AbstractBridge may have additional requirements. Consult their docstrings for details.

In addition, all subtypes may optionally implement the following constraint attributes with the bridge in place of the constraint index:

source
MathOptInterface.Bridges.added_constrained_variable_typesFunction
added_constrained_variable_types(
+API Reference · MathOptInterface

Bridges

AbstractBridge API

MathOptInterface.Bridges.AbstractBridgeType
abstract type AbstractBridge end

An abstract type representing a bridged constraint or variable in a MOI.Bridges.AbstractBridgeOptimizer.

All bridges must implement:

Subtypes of AbstractBridge may have additional requirements. Consult their docstrings for details.

In addition, all subtypes may optionally implement the following constraint attributes with the bridge in place of the constraint index:

source
MathOptInterface.Bridges.added_constrained_variable_typesFunction
added_constrained_variable_types(
     BT::Type{<:AbstractBridge},
 )::Vector{Tuple{Type}}

Return a list of the types of constrained variables that bridges of concrete type BT add.

Implementation notes

  • This method depends only on the type of the bridge, not the runtime value. If the bridge may add a constrained variable, the type must be included in the return vector.
  • If the bridge adds a free variable via MOI.add_variable or MOI.add_variables, the return vector must include (MOI.Reals,).

Example

julia> MOI.Bridges.added_constrained_variable_types(
            MOI.Bridges.Variable.NonposToNonnegBridge{Float64},
        )
 1-element Vector{Tuple{Type}}:
- (MathOptInterface.Nonnegatives,)
source
MathOptInterface.Bridges.added_constraint_typesFunction
added_constraint_types(
     BT::Type{<:AbstractBridge},
 )::Vector{Tuple{Type,Type}}

Return a list of the types of constraints that bridges of concrete type BT add.

Implementation notes

  • This method depends only on the type of the bridge, not the runtime value. If the bridge may add a constraint, the type must be included in the return vector.

Example

julia> MOI.Bridges.added_constraint_types(
            MOI.Bridges.Constraint.ZeroOneBridge{Float64},
        )
 2-element Vector{Tuple{Type, Type}}:
  (MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.Interval{Float64})
- (MathOptInterface.VariableIndex, MathOptInterface.Integer)
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.NumberOfVariables)::Int64

Return the number of variables created by the bridge b in the model.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.ListOfVariableIndices)

Return the list of variables created by the bridge b.

See also MOI.ListOfVariableIndices.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints{F,S})::Int64 where {F,S}

Return the number of constraints of the type F-in-S created by the bridge b.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.ListOfConstraintIndices{F,S}) where {F,S}

Return a Vector{ConstraintIndex{F,S}} with indices of all constraints of type F-in-S created by the bride b.

See also MOI.ListOfConstraintIndices.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MathOptInterface.Bridges.final_touchFunction
final_touch(bridge::AbstractBridge, model::MOI.ModelLike)::Nothing

A function that is called immediately prior to MOI.optimize! to allow bridges to modify their reformulations with respect to other variables and constraints in model.

For example, if the correctness of bridge depends on the bounds of a variable or the fact that variables are integer, then the bridge can implement final_touch to check assumptions immediately before a call to MOI.optimize!.

If you implement this method, you must also implement needs_final_touch.

source
MathOptInterface.Bridges.bridging_costFunction
bridging_cost(b::AbstractBridgeOptimizer, S::Type{<:MOI.AbstractSet}})

Return the cost of bridging variables constrained in S on creation, is_bridged(b, S) is assumed to be true.

bridging_cost(
+ (MathOptInterface.VariableIndex, MathOptInterface.Integer)
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.NumberOfVariables)::Int64

Return the number of variables created by the bridge b in the model.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.ListOfVariableIndices)

Return the list of variables created by the bridge b.

See also MOI.ListOfVariableIndices.

Implementation notes

  • There is a default fallback, so you need only implement this if the bridge adds new variables.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints{F,S})::Int64 where {F,S}

Return the number of constraints of the type F-in-S created by the bridge b.

See also MOI.NumberOfConstraints.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MathOptInterface.getMethod
MOI.get(b::AbstractBridge, ::MOI.ListOfConstraintIndices{F,S}) where {F,S}

Return a Vector{ConstraintIndex{F,S}} with indices of all constraints of type F-in-S created by the bride b.

See also MOI.ListOfConstraintIndices.

Implementation notes

  • There is a default fallback, so you need only implement this for the constraint types returned by added_constraint_types.
source
MathOptInterface.Bridges.final_touchFunction
final_touch(bridge::AbstractBridge, model::MOI.ModelLike)::Nothing

A function that is called immediately prior to MOI.optimize! to allow bridges to modify their reformulations with respect to other variables and constraints in model.

For example, if the correctness of bridge depends on the bounds of a variable or the fact that variables are integer, then the bridge can implement final_touch to check assumptions immediately before a call to MOI.optimize!.

If you implement this method, you must also implement needs_final_touch.

source
MathOptInterface.Bridges.bridging_costFunction
bridging_cost(b::AbstractBridgeOptimizer, S::Type{<:MOI.AbstractSet}})

Return the cost of bridging variables constrained in S on creation, is_bridged(b, S) is assumed to be true.

bridging_cost(
     b::AbstractBridgeOptimizer,
     F::Type{<:MOI.AbstractFunction},
     S::Type{<:MOI.AbstractSet},
-)

Return the cost of bridging F-in-S constraints.

is_bridged(b, S) is assumed to be true.

source
MathOptInterface.Bridges.runtestsFunction
runtests(
     Bridge::Type{<:AbstractBridge},
     input_fn::Function,
     output_fn::Function;
@@ -31,7 +31,7 @@
                x, _ = MOI.add_constrained_variable(model, MOI.Integer())
                MOI.add_constraint(model, 1.0 * x, MOI.Interval(0.0, 1.0))
            end,
-       )
source
runtests(
+       )
source
runtests(
     Bridge::Type{<:AbstractBridge},
     input::String,
     output::String;
@@ -49,7 +49,7 @@
            x in Integer()
            1.0 * x in Interval(0.0, 1.0)
            """,
-       )
source

Constraint bridge API

MathOptInterface.Bridges.Constraint.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return AbstractBridgeOptimizer that always bridges any objective function supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the objective function if it is supported by the bridge BT and unsupported by model.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Constraint.AbstractBridge end
+       )
source

Constraint bridge API

MathOptInterface.Bridges.Constraint.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return AbstractBridgeOptimizer that always bridges any objective function supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the objective function if it is supported by the bridge BT and unsupported by model.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Constraint.AbstractBridge end
 
 julia> bridge = MOI.Bridges.Constraint.SingleBridgeOptimizer{MyNewBridge{Float64}}(
            MOI.Utilities.Model{Float64}(),
@@ -59,11 +59,11 @@
 ├ ObjectiveFunctionType: MOI.ScalarAffineFunction{Float64}
 ├ NumberOfVariables: 0
 └ NumberOfConstraints: 0

Implementation notes

All bridges should simplify the creation of SingleBridgeOptimizers by defining a constant that wraps the bridge in a SingleBridgeOptimizer.

julia> const MyNewBridgeModel{T,OT<:MOI.ModelLike} =
-           MOI.Bridges.Constraint.SingleBridgeOptimizer{MyNewBridge{T},OT};

This enables users to create bridged models as follows:

julia> MyNewBridgeModel{Float64}(MOI.Utilities.Model{Float64}());
source
MathOptInterface.supports_constraintMethod
MOI.supports_constraint(
+           MOI.Bridges.Constraint.SingleBridgeOptimizer{MyNewBridge{T},OT};

This enables users to create bridged models as follows:

julia> MyNewBridgeModel{Float64}(MOI.Utilities.Model{Float64}());
source
MathOptInterface.supports_constraintMethod
MOI.supports_constraint(
     BT::Type{<:AbstractBridge},
     F::Type{<:MOI.AbstractFunction},
     S::Type{<:MOI.AbstractSet},
-)::Bool

Return a Bool indicating whether the bridges of type BT support bridging F-in-S constraints.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method for constraint types that the bridge implements.
source
MathOptInterface.Bridges.Constraint.concrete_bridge_typeFunction
concrete_bridge_type(
+)::Bool

Return a Bool indicating whether the bridges of type BT support bridging F-in-S constraints.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method for constraint types that the bridge implements.
source
MathOptInterface.Bridges.Constraint.concrete_bridge_typeFunction
concrete_bridge_type(
     BT::Type{<:AbstractBridge},
     F::Type{<:MOI.AbstractFunction},
     S::Type{<:MOI.AbstractSet}
@@ -72,40 +72,40 @@
            MOI.VariableIndex,
            MOI.Interval{Float64},
        )
-MathOptInterface.Bridges.Constraint.SplitIntervalBridge{Float64, MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}, MathOptInterface.GreaterThan{Float64}, MathOptInterface.LessThan{Float64}}
source
MathOptInterface.Bridges.Constraint.bridge_constraintFunction
bridge_constraint(
+MathOptInterface.Bridges.Constraint.SplitIntervalBridge{Float64, MathOptInterface.VariableIndex, MathOptInterface.Interval{Float64}, MathOptInterface.GreaterThan{Float64}, MathOptInterface.LessThan{Float64}}
source
MathOptInterface.Bridges.Constraint.bridge_constraintFunction
bridge_constraint(
     BT::Type{<:AbstractBridge},
     model::MOI.ModelLike,
     func::AbstractFunction,
     set::MOI.AbstractSet,
-)::BT

Bridge the constraint func-in-set using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT should be a concrete type, that is, all the type parameters of the bridge must be set.
source
MathOptInterface.Bridges.Constraint.conversion_costFunction
conversion_cost(
+)::BT

Bridge the constraint func-in-set using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT should be a concrete type, that is, all the type parameters of the bridge must be set.
source

Objective bridge API

MathOptInterface.Bridges.Objective.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return AbstractBridgeOptimizer that always bridges any objective function supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the objective function if it is supported by the bridge BT and unsupported by model.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Objective.AbstractBridge end
+)::Float64

Return a Float64 returning the cost of converting any function of type G to a function of type F with convert.

This cost is used to compute MOI.Bridges.bridging_cost.

The default cost is Inf, which means that MOI.Bridges.Constraint.FunctionConversionBridge should not attempt the conversion.

source

Objective bridge API

MathOptInterface.Bridges.Objective.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return AbstractBridgeOptimizer that always bridges any objective function supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the objective function if it is supported by the bridge BT and unsupported by model.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Objective.AbstractBridge end
 
 julia> bridge = MOI.Bridges.Objective.SingleBridgeOptimizer{MyNewBridge{Float64}}(
            MOI.Utilities.Model{Float64}(),
        );

Implementation notes

All bridges should simplify the creation of SingleBridgeOptimizers by defining a constant that wraps the bridge in a SingleBridgeOptimizer.

julia> const MyNewBridgeModel{T,OT<:MOI.ModelLike} =
-           MOI.Bridges.Objective.SingleBridgeOptimizer{MyNewBridge{T},OT};

This enables users to create bridged models as follows:

julia> MyNewBridgeModel{Float64}(MOI.Utilities.Model{Float64}());
source
MathOptInterface.Bridges.Objective.supports_objective_functionFunction
supports_objective_function(
     BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
     F::Type{<:MOI.AbstractFunction},
-)::Bool

Return a Bool indicating whether the bridges of type BT support bridging objective functions of type F.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method For objective functions that the bridge implements.
source
MathOptInterface.Bridges.set_objective_function_typeFunction
set_objective_function_type(
+)::Bool

Return a Bool indicating whether the bridges of type BT support bridging objective functions of type F.

Implementation notes

  • This method depends only on the type of the inputs, not the runtime values.
  • There is a default fallback, so you need only implement this method For objective functions that the bridge implements.
source
MathOptInterface.Bridges.set_objective_function_typeFunction
set_objective_function_type(
     BT::Type{<:Objective.AbstractBridge},
 )::Type{<:MOI.AbstractFunction}

Return the type of objective function that bridges of concrete type BT set.

Implementation notes

  • This method depends only on the type of the bridge, not the runtime value.

Example

julia> MOI.Bridges.set_objective_function_type(
            MOI.Bridges.Objective.FunctionizeBridge{Float64},
        )
-MathOptInterface.ScalarAffineFunction{Float64}
source
MathOptInterface.Bridges.Objective.concrete_bridge_typeFunction
concrete_bridge_type(
     BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
     F::Type{<:MOI.AbstractFunction},
-)::Type

Return the concrete type of the bridge supporting objective functions of type F.

This function can only be called if MOI.supports_objective_function(BT, F) is true.

source
MathOptInterface.Bridges.Objective.bridge_objectiveFunction
bridge_objective(
     BT::Type{<:MOI.Bridges.Objective.AbstractBridge},
     model::MOI.ModelLike,
     func::MOI.AbstractFunction,
-)::BT

Bridge the objective function func using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT must be a concrete type, that is, all the type parameters of the bridge must be set.
source

Variable bridge API

MathOptInterface.Bridges.Variable.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return MOI.Bridges.AbstractBridgeOptimizer that always bridges any variables constrained on creation supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the variables constrained on creation if they are supported by the bridge BT and unsupported by model.

Warning

Two SingleBridgeOptimizers cannot be used together as both of them assume that the underlying model only returns variable indices with nonnegative values. Use MOI.Bridges.LazyBridgeOptimizer instead.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Variable.AbstractBridge end
+)::BT

Bridge the objective function func using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT must be a concrete type, that is, all the type parameters of the bridge must be set.
source

Variable bridge API

MathOptInterface.Bridges.Variable.SingleBridgeOptimizerType
SingleBridgeOptimizer{BT<:AbstractBridge}(model::MOI.ModelLike)

Return MOI.Bridges.AbstractBridgeOptimizer that always bridges any variables constrained on creation supported by the bridge BT.

This is in contrast with the MOI.Bridges.LazyBridgeOptimizer, which only bridges the variables constrained on creation if they are supported by the bridge BT and unsupported by model.

Warning

Two SingleBridgeOptimizers cannot be used together as both of them assume that the underlying model only returns variable indices with nonnegative values. Use MOI.Bridges.LazyBridgeOptimizer instead.

Example

julia> struct MyNewBridge{T} <: MOI.Bridges.Variable.AbstractBridge end
 
 julia> bridge = MOI.Bridges.Variable.SingleBridgeOptimizer{MyNewBridge{Float64}}(
            MOI.Utilities.Model{Float64}(),
        );

Implementation notes

All bridges should simplify the creation of SingleBridgeOptimizers by defining a constant that wraps the bridge in a SingleBridgeOptimizer.

julia> const MyNewBridgeModel{T,OT<:MOI.ModelLike} =
-           MOI.Bridges.Variable.SingleBridgeOptimizer{MyNewBridge{T},OT};

This enables users to create bridged models as follows:

julia> MyNewBridgeModel{Float64}(MOI.Utilities.Model{Float64}());
source
MathOptInterface.Bridges.Variable.supports_constrained_variableFunction
supports_constrained_variable(
     BT::Type{<:AbstractBridge},
     S::Type{<:MOI.AbstractSet},
 )::Bool

Return a Bool indicating whether the bridges of type BT support bridging constrained variables in S. That is, it returns true if the bridge of type BT converts constrained variables of type S into a form supported by the solver.

Implementation notes

  • This method depends only on the type of the bridge and set, not the runtime values.
  • There is a default fallback, so you need only implement this method for sets that the bridge implements.

Example

julia> MOI.Bridges.Variable.supports_constrained_variable(
@@ -118,7 +118,7 @@
            MOI.Bridges.Variable.NonposToNonnegBridge{Float64},
            MOI.Nonnegatives,
        )
-false
source
MathOptInterface.Bridges.Variable.concrete_bridge_typeFunction
concrete_bridge_type(
     BT::Type{<:AbstractBridge},
     S::Type{<:MOI.AbstractSet},
 )::Type

Return the concrete type of the bridge supporting variables in S constraints.

This function can only be called if MOI.supports_constrained_variable(BT, S) is true.

Example

As a variable in MOI.GreaterThan is bridged into variables in MOI.Nonnegatives by the VectorizeBridge:

julia> import MathOptInterface as MOI
@@ -127,11 +127,11 @@
            MOI.Bridges.Variable.VectorizeBridge{Float64},
            MOI.GreaterThan{Float64},
        )
-MathOptInterface.Bridges.Variable.VectorizeBridge{Float64, MathOptInterface.Nonnegatives}
source
MathOptInterface.Bridges.Variable.bridge_constrained_variableFunction
bridge_constrained_variable(
     BT::Type{<:AbstractBridge},
     model::MOI.ModelLike,
     set::MOI.AbstractSet,
-)::BT

Bridge the constrained variable in set using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT must be a concrete type, that is, all the type parameters of the bridge must be set.
source
MathOptInterface.Bridges.Variable.unbridged_mapFunction
unbridged_map(
+)::BT

Bridge the constrained variable in set using bridge BT to model and returns a bridge object of type BT.

Implementation notes

  • The bridge type BT must be a concrete type, that is, all the type parameters of the bridge must be set.
source
MathOptInterface.Bridges.Variable.unbridged_mapFunction
unbridged_map(
    bridge::MOI.Bridges.Variable.AbstractBridge,
     vi::MOI.VariableIndex,
 )

For a bridged variable in a scalar set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged variable vi.

unbridged_map(
@@ -141,20 +141,20 @@
     bridge::MOI.Bridges.Variable.AbstractBridge,
     vi::MOI.VariableIndex,
     i::MOI.Bridges.IndexInVector,
-)

For a bridged variable in a vector set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged variable vi corresponding to the ith variable of the vector.

If there is no way to recover the expression in terms of the bridged variable(s) vi(s), return nothing. See ZerosBridge for an example of bridge returning nothing.

source

AbstractBridgeOptimizer API

MathOptInterface.Bridges.bridged_variable_functionFunction
bridged_variable_function(
+)

For a bridged variable in a vector set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged variable vi corresponding to the ith variable of the vector.

If there is no way to recover the expression in terms of the bridged variable(s) vi(s), return nothing. See ZerosBridge for an example of bridge returning nothing.

source

AbstractBridgeOptimizer API

MathOptInterface.Bridges.bridged_variable_functionFunction
bridged_variable_function(
     b::AbstractBridgeOptimizer,
     vi::MOI.VariableIndex,
-)

Return a MOI.AbstractScalarFunction of variables of b.model that equals vi. That is, if the variable vi is bridged, it returns its expression in terms of the variables of b.model. Otherwise, it returns vi.

source
MathOptInterface.Bridges.unbridged_variable_functionFunction
unbridged_variable_function(
+)

Return a MOI.AbstractScalarFunction of variables of b.model that equals vi. That is, if the variable vi is bridged, it returns its expression in terms of the variables of b.model. Otherwise, it returns vi.

source
MathOptInterface.Bridges.unbridged_variable_functionFunction
unbridged_variable_function(
     b::AbstractBridgeOptimizer,
     vi::MOI.VariableIndex,
-)

Return a MOI.AbstractScalarFunction of variables of b that equals vi. That is, if the variable vi is an internal variable of b.model created by a bridge but not visible to the user, it returns its expression in terms of the variables of bridged variables. Otherwise, it returns vi.

source
MathOptInterface.Bridges.recursive_modelFunction
recursive_model(b::AbstractBridgeOptimizer)

If a variable, constraint, or objective is bridged, return the context of the inner variables. For most optimizers, this should be b.model.

source
MathOptInterface.Bridges.FirstBridgeType
struct FirstBridge <: MOI.AbstractConstraintAttribute end

Returns the first bridge used to bridge the constraint.

Warning

The indices of the bridge correspond to internal indices and may not correspond to indices of the model this attribute is got from.

source

LazyBridgeOptimizer API

MathOptInterface.Bridges.LazyBridgeOptimizerType
LazyBridgeOptimizer(model::MOI.ModelLike)

The LazyBridgeOptimizer is a bridge optimizer that supports multiple bridges, and only bridges things which are not supported by the internal model.

Internally, the LazyBridgeOptimizer solves a shortest hyper-path problem to determine which bridges to use.

In general, you should use full_bridge_optimizer instead of this constructor because full_bridge_optimizer automatically adds a large number of supported bridges.

See also: add_bridge, remove_bridge, has_bridge and full_bridge_optimizer.

Example

julia> model = MOI.Bridges.LazyBridgeOptimizer(MOI.Utilities.Model{Float64}());
+)

Return a MOI.AbstractScalarFunction of variables of b that equals vi. That is, if the variable vi is an internal variable of b.model created by a bridge but not visible to the user, it returns its expression in terms of the variables of bridged variables. Otherwise, it returns vi.

source
MathOptInterface.Bridges.recursive_modelFunction
recursive_model(b::AbstractBridgeOptimizer)

If a variable, constraint, or objective is bridged, return the context of the inner variables. For most optimizers, this should be b.model.

source
MathOptInterface.Bridges.FirstBridgeType
struct FirstBridge <: MOI.AbstractConstraintAttribute end

Returns the first bridge used to bridge the constraint.

Warning

The indices of the bridge correspond to internal indices and may not correspond to indices of the model this attribute is got from.

source

LazyBridgeOptimizer API

MathOptInterface.Bridges.LazyBridgeOptimizerType
LazyBridgeOptimizer(model::MOI.ModelLike)

The LazyBridgeOptimizer is a bridge optimizer that supports multiple bridges, and only bridges things which are not supported by the internal model.

Internally, the LazyBridgeOptimizer solves a shortest hyper-path problem to determine which bridges to use.

In general, you should use full_bridge_optimizer instead of this constructor because full_bridge_optimizer automatically adds a large number of supported bridges.

See also: add_bridge, remove_bridge, has_bridge and full_bridge_optimizer.

Example

julia> model = MOI.Bridges.LazyBridgeOptimizer(MOI.Utilities.Model{Float64}());
 
 julia> MOI.Bridges.add_bridge(model, MOI.Bridges.Variable.FreeBridge{Float64})
 
 julia> MOI.Bridges.has_bridge(model, MOI.Bridges.Variable.FreeBridge{Float64})
-true
source
MathOptInterface.Bridges.full_bridge_optimizerFunction
full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where {T}

Returns a LazyBridgeOptimizer bridging model for every bridge defined in this package (see below for the few exceptions) and for the coefficient type T, as well as the bridges in the list returned by the ListOfNonstandardBridges attribute.

Example

julia> model = MOI.Utilities.Model{Float64}();
 
-julia> bridged_model = MOI.Bridges.full_bridge_optimizer(model, Float64);

Exceptions

The following bridges are not added by full_bridge_optimizer, except if they are in the list returned by the ListOfNonstandardBridges attribute:

See the docstring of the each bridge for the reason they are not added.

source
MathOptInterface.Bridges.ListOfNonstandardBridgesType
ListOfNonstandardBridges{T}() <: MOI.AbstractOptimizerAttribute

Any optimizer can be wrapped in a LazyBridgeOptimizer using full_bridge_optimizer. However, by default LazyBridgeOptimizer uses a limited set of bridges that are:

  1. implemented in MOI.Bridges
  2. generally applicable for all optimizers.

For some optimizers however, it is useful to add additional bridges, such as those that are implemented in external packages (for example, within the solver package itself) or only apply in certain circumstances (for example, Constraint.SOCtoNonConvexQuadBridge).

Such optimizers should implement the ListOfNonstandardBridges attribute to return a vector of bridge types that are added by full_bridge_optimizer in addition to the list of default bridges.

Note that optimizers implementing ListOfNonstandardBridges may require package-specific functions or sets to be used if the non-standard bridges are not added. Therefore, you are recommended to use model = MOI.instantiate(Package.Optimizer; with_bridge_type = T) instead of model = MOI.instantiate(Package.Optimizer). See MOI.instantiate.

Example

An optimizer using a non-default bridge in MOI.Bridges

Solvers supporting MOI.ScalarQuadraticFunction can support MOI.SecondOrderCone and MOI.RotatedSecondOrderCone by defining:

function MOI.get(::MyQuadraticOptimizer, ::ListOfNonstandardBridges{Float64})
+julia> bridged_model = MOI.Bridges.full_bridge_optimizer(model, Float64);

Exceptions

The following bridges are not added by full_bridge_optimizer, except if they are in the list returned by the ListOfNonstandardBridges attribute:

See the docstring of the each bridge for the reason they are not added.

source
MathOptInterface.Bridges.ListOfNonstandardBridgesType
ListOfNonstandardBridges{T}() <: MOI.AbstractOptimizerAttribute

Any optimizer can be wrapped in a LazyBridgeOptimizer using full_bridge_optimizer. However, by default LazyBridgeOptimizer uses a limited set of bridges that are:

  1. implemented in MOI.Bridges
  2. generally applicable for all optimizers.

For some optimizers however, it is useful to add additional bridges, such as those that are implemented in external packages (for example, within the solver package itself) or only apply in certain circumstances (for example, Constraint.SOCtoNonConvexQuadBridge).

Such optimizers should implement the ListOfNonstandardBridges attribute to return a vector of bridge types that are added by full_bridge_optimizer in addition to the list of default bridges.

Note that optimizers implementing ListOfNonstandardBridges may require package-specific functions or sets to be used if the non-standard bridges are not added. Therefore, you are recommended to use model = MOI.instantiate(Package.Optimizer; with_bridge_type = T) instead of model = MOI.instantiate(Package.Optimizer). See MOI.instantiate.

Example

An optimizer using a non-default bridge in MOI.Bridges

Solvers supporting MOI.ScalarQuadraticFunction can support MOI.SecondOrderCone and MOI.RotatedSecondOrderCone by defining:

function MOI.get(::MyQuadraticOptimizer, ::ListOfNonstandardBridges{Float64})
     return Type[
         MOI.Bridges.Constraint.SOCtoNonConvexQuadBridge{Float64},
         MOI.Bridges.Constraint.RSOCtoNonConvexQuadBridge{Float64},
@@ -167,54 +167,54 @@
 end
 # ...

from VectorAffineFunction{T} to the MatrixAffineFunction. Finally, it defines:

function MOI.get(::Optimizer{T}, ::ListOfNonstandardBridges{T}) where {T}
     return Type[MatrixAffineFunctionBridge{T}]
-end
source
MathOptInterface.Bridges.print_active_bridgesFunction
print_active_bridges([io::IO=stdout,] b::MOI.Bridges.LazyBridgeOptimizer)

Print the set of bridges that are active in the model b.

source
print_active_bridges(
     [io::IO=stdout,]
     b::MOI.Bridges.LazyBridgeOptimizer,
     F::Type{<:MOI.AbstractFunction}
-)

Print the set of bridges required for an objective function of type F.

source
print_active_bridges(
+)

Print the set of bridges required for an objective function of type F.

source
print_active_bridges(
     [io::IO=stdout,]
     b::MOI.Bridges.LazyBridgeOptimizer,
     F::Type{<:MOI.AbstractFunction},
     S::Type{<:MOI.AbstractSet},
-)

Print the set of bridges required for a constraint of type F-in-S.

source
print_active_bridges(
+)

Print the set of bridges required for a constraint of type F-in-S.

source
print_active_bridges(
     [io::IO=stdout,]
     b::MOI.Bridges.LazyBridgeOptimizer,
     S::Type{<:MOI.AbstractSet}
-)

Print the set of bridges required for a variable constrained to set S.

source
MathOptInterface.Bridges.print_graphFunction
print_graph([io::IO = stdout,] b::LazyBridgeOptimizer)

Print the hyper-graph containing all variable, constraint, and objective types that could be obtained by bridging the variables, constraints, and objectives that are present in the model by all the bridges added to b.

Each node in the hyper-graph corresponds to a variable, constraint, or objective type.

  • Variable nodes are indicated by [ ]
  • Constraint nodes are indicated by ( )
  • Objective nodes are indicated by | |

The number inside each pair of brackets is an index of the node in the hyper-graph.

Note that this hyper-graph is the full list of possible transformations. When the bridged model is created, we select the shortest hyper-path from this graph, so many nodes may be un-used.

To see which nodes are used, call print_active_bridges.

For more information, see Legat, B., Dowson, O., Garcia, J., and Lubin, M. (2020). "MathOptInterface: a data structure for mathematical optimization problems." URL

source
MathOptInterface.Bridges.print_graphFunction
print_graph([io::IO = stdout,] b::LazyBridgeOptimizer)

Print the hyper-graph containing all variable, constraint, and objective types that could be obtained by bridging the variables, constraints, and objectives that are present in the model by all the bridges added to b.

Each node in the hyper-graph corresponds to a variable, constraint, or objective type.

  • Variable nodes are indicated by [ ]
  • Constraint nodes are indicated by ( )
  • Objective nodes are indicated by | |

The number inside each pair of brackets is an index of the node in the hyper-graph.

Note that this hyper-graph is the full list of possible transformations. When the bridged model is created, we select the shortest hyper-path from this graph, so many nodes may be un-used.

To see which nodes are used, call print_active_bridges.

For more information, see Legat, B., Dowson, O., Garcia, J., and Lubin, M. (2020). "MathOptInterface: a data structure for mathematical optimization problems." URL

source

SetMap API

SetMap API

MathOptInterface.Bridges.map_functionFunction
map_function(bridge::MOI.Bridges.AbstractBridge, func)
-map_function(::Type{BT}, func) where {BT}

Return the image of func through the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintPrimal of variable bridges. For constraint bridges, this is used for bridging the constraint, setting the MOI.ConstraintFunction and MOI.ConstraintPrimalStart and modifying the function with MOI.modify.

The default implementation of Constraint.bridge_constraint uses map_function with the bridge type so if this function is defined on the bridge type, Constraint.bridge_constraint does not need to be implemented.

source
map_function(::Type{BT}, func, i::IndexInVector) where {BT}

Return the scalar function at the ith index of the vector function that would be returned by map_function(BT, func) except that it may compute the ith element. This is used by bridged_function and for getting the MOI.VariablePrimal and MOI.VariablePrimalStart of variable bridges.

source
MathOptInterface.Bridges.inverse_map_functionFunction
inverse_map_function(bridge::MOI.Bridges.AbstractBridge, func)
-inverse_map_function(::Type{BT}, func) where {BT}

Return the image of func through the inverse of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used by Variable.unbridged_map and for setting the MOI.VariablePrimalStart of variable bridges and for getting the MOI.ConstraintFunction, the MOI.ConstraintPrimal and the MOI.ConstraintPrimalStart of constraint bridges.

If the linear map A is not invertible, the error MapNotInvertible is thrown.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source
MathOptInterface.Bridges.adjoint_map_functionFunction
adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
-adjoint_map_function(::Type{BT}, func) where {BT}

Return the image of func through the adjoint of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintDual and MOI.ConstraintDualStart of constraint bridges.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source
MathOptInterface.Bridges.inverse_adjoint_map_functionFunction
inverse_adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
-inverse_adjoint_map_function(::Type{BT}, func) where {BT}

Return the image of func through the inverse of the adjoint of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintDual of variable bridges and setting the MOI.ConstraintDualStart of constraint bridges.

If the linear map A is not invertible, the error MapNotInvertible is thrown.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source

Bridging graph API

MathOptInterface.Bridges.GraphType
Graph()

A type-stable datastructure for computing the shortest hyperpath problem.

Nodes

There are three types of nodes in the graph:

Add nodes to the graph using add_node.

Edges

There are two types of edges in the graph:

Add edges to the graph using add_edge.

For the ability to add a variable constrained on creation as a free variable followed by a constraint, use set_variable_constraint_node.

Optimal hyper-edges

Use bridge_index to compute the minimum-cost bridge leaving a node.

Note that bridge_index lazy runs a Bellman-Ford algorithm to compute the set of minimum cost edges. Thus, the first call to bridge_index after adding new nodes or edges will take longer than subsequent calls.

source
MathOptInterface.Bridges.map_functionFunction
map_function(bridge::MOI.Bridges.AbstractBridge, func)
+map_function(::Type{BT}, func) where {BT}

Return the image of func through the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintPrimal of variable bridges. For constraint bridges, this is used for bridging the constraint, setting the MOI.ConstraintFunction and MOI.ConstraintPrimalStart and modifying the function with MOI.modify.

The default implementation of Constraint.bridge_constraint uses map_function with the bridge type so if this function is defined on the bridge type, Constraint.bridge_constraint does not need to be implemented.

source
map_function(::Type{BT}, func, i::IndexInVector) where {BT}

Return the scalar function at the ith index of the vector function that would be returned by map_function(BT, func) except that it may compute the ith element. This is used by bridged_function and for getting the MOI.VariablePrimal and MOI.VariablePrimalStart of variable bridges.

source
MathOptInterface.Bridges.inverse_map_functionFunction
inverse_map_function(bridge::MOI.Bridges.AbstractBridge, func)
+inverse_map_function(::Type{BT}, func) where {BT}

Return the image of func through the inverse of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used by Variable.unbridged_map and for setting the MOI.VariablePrimalStart of variable bridges and for getting the MOI.ConstraintFunction, the MOI.ConstraintPrimal and the MOI.ConstraintPrimalStart of constraint bridges.

If the linear map A is not invertible, the error MapNotInvertible is thrown.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source
MathOptInterface.Bridges.adjoint_map_functionFunction
adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
+adjoint_map_function(::Type{BT}, func) where {BT}

Return the image of func through the adjoint of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintDual and MOI.ConstraintDualStart of constraint bridges.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source
MathOptInterface.Bridges.inverse_adjoint_map_functionFunction
inverse_adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
+inverse_adjoint_map_function(::Type{BT}, func) where {BT}

Return the image of func through the inverse of the adjoint of the linear map A defined in Variable.SetMapBridge and Constraint.SetMapBridge.

This function is used for getting the MOI.ConstraintDual of variable bridges and setting the MOI.ConstraintDualStart of constraint bridges.

If the linear map A is not invertible, the error MapNotInvertible is thrown.

The method can alternatively be defined on the bridge type. This legacy interface is kept for backward compatibility.

source

Bridging graph API

MathOptInterface.Bridges.GraphType
Graph()

A type-stable datastructure for computing the shortest hyperpath problem.

Nodes

There are three types of nodes in the graph:

Add nodes to the graph using add_node.

Edges

There are two types of edges in the graph:

Add edges to the graph using add_edge.

For the ability to add a variable constrained on creation as a free variable followed by a constraint, use set_variable_constraint_node.

Optimal hyper-edges

Use bridge_index to compute the minimum-cost bridge leaving a node.

Note that bridge_index lazy runs a Bellman-Ford algorithm to compute the set of minimum cost edges. Thus, the first call to bridge_index after adding new nodes or edges will take longer than subsequent calls.

source
MathOptInterface.Bridges.add_nodeFunction
add_node(graph::Graph, ::Type{VariableNode})::VariableNode
 add_node(graph::Graph, ::Type{ConstraintNode})::ConstraintNode
-add_node(graph::Graph, ::Type{ObjectiveNode})::ObjectiveNode

Add a new node to graph.

source
MathOptInterface.Bridges.add_edgeFunction
add_edge(graph::Graph, node::VariableNode, edge::Edge)::Nothing
+add_node(graph::Graph, ::Type{ObjectiveNode})::ObjectiveNode

Add a new node to graph.

source
MathOptInterface.Bridges.add_edgeFunction
add_edge(graph::Graph, node::VariableNode, edge::Edge)::Nothing
 add_edge(graph::Graph, node::ConstraintNode, edge::Edge)::Nothing
-add_edge(graph::Graph, node::ObjectiveNode, edge::ObjectiveEdge)::Nothing

Add edge to graph, where edge starts at node and connects to the nodes defined in edge.

source
MathOptInterface.Bridges.set_variable_constraint_nodeFunction
set_variable_constraint_node(
     graph::Graph,
     variable_node::VariableNode,
     constraint_node::ConstraintNode,
     cost::Int,
-)

As an alternative to variable_node, add a virtual edge to graph that represents adding a free variable, followed by a constraint of type constraint_node, with bridging cost cost.

Why is this needed?

Variables can either be added as a variable constrained on creation, or as a free variable which then has a constraint added to it.

source
MathOptInterface.Bridges.bridge_indexFunction
bridge_index(graph::Graph, node::VariableNode)::Int
+)

As an alternative to variable_node, add a virtual edge to graph that represents adding a free variable, followed by a constraint of type constraint_node, with bridging cost cost.

Why is this needed?

Variables can either be added as a variable constrained on creation, or as a free variable which then has a constraint added to it.

source
MathOptInterface.Bridges.bridge_indexFunction
bridge_index(graph::Graph, node::VariableNode)::Int
 bridge_index(graph::Graph, node::ConstraintNode)::Int
-bridge_index(graph::Graph, node::ObjectiveNode)::Int

Return the optimal index of the bridge to chose from node.

source
MathOptInterface.Bridges.is_variable_edge_bestFunction
is_variable_edge_best(graph::Graph, node::VariableNode)::Bool

Return a Bool indicating whether node should be added as a variable constrained on creation, or as a free variable followed by a constraint.

source
+bridge_index(graph::Graph, node::ObjectiveNode)::Int

Return the optimal index of the bridge to chose from node.

source
MathOptInterface.Bridges.is_variable_edge_bestFunction
is_variable_edge_best(graph::Graph, node::VariableNode)::Bool

Return a Bool indicating whether node should be added as a variable constrained on creation, or as a free variable followed by a constraint.

source
diff --git a/dev/submodules/FileFormats/overview/index.html b/dev/submodules/FileFormats/overview/index.html index a2af67a684..b5a795f98d 100644 --- a/dev/submodules/FileFormats/overview/index.html +++ b/dev/submodules/FileFormats/overview/index.html @@ -154,4 +154,4 @@ path: [variables][1] instance: Dict{String, Any}("NaMe" => "x") schema key: required -schema value: Any["name"] +schema value: Any["name"] diff --git a/dev/submodules/FileFormats/reference/index.html b/dev/submodules/FileFormats/reference/index.html index 0819ccf814..bc78a2779d 100644 --- a/dev/submodules/FileFormats/reference/index.html +++ b/dev/submodules/FileFormats/reference/index.html @@ -4,7 +4,7 @@ format::FileFormat = FORMAT_AUTOMATIC, filename::Union{Nothing, String} = nothing, kwargs... -)

Return model corresponding to the FileFormat format, or, if format == FORMAT_AUTOMATIC, guess the format from filename.

The filename argument is only needed if format == FORMAT_AUTOMATIC.

kwargs are passed to the underlying model constructor.

source
MathOptInterface.FileFormats.FileFormatType
FileFormat

List of accepted export formats.

  • FORMAT_AUTOMATIC: try to detect the file format based on the file name
  • FORMAT_CBF: the Conic Benchmark format
  • FORMAT_LP: the LP file format
  • FORMAT_MOF: the MathOptFormat file format
  • FORMAT_MPS: the MPS file format
  • FORMAT_NL: the AMPL .nl file format
  • FORMAT_REW: the .rew file format, which is MPS with generic names
  • FORMAT_SDPA: the SemiDefinite Programming Algorithm format
source
MathOptInterface.FileFormats.LP.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.LP.Model.

Keyword arguments are:

  • maximum_length::Int=255: the maximum length for the name of a variable. lp_solve 5.0 allows only 16 characters, while CPLEX 12.5+ allow 255.

  • warn::Bool=false: print a warning when variables or constraints are renamed.

source
MathOptInterface.FileFormats.MOF.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.MOF.Model.

Keyword arguments are:

  • print_compact::Bool=false: print the JSON file in a compact format without spaces or newlines.
  • warn::Bool=false: print a warning when variables or constraints are renamed
  • differentiation_backend::MOI.Nonlinear.AbstractAutomaticDifferentiation = MOI.Nonlinear.SparseReverseMode(): automatic differentiation backend to use when reading models with nonlinear constraints and objectives.
  • use_nlp_block::Bool=true: if true parse "ScalarNonlinearFunction" into an MOI.NLPBlock. If false, "ScalarNonlinearFunction" are parsed as MOI.ScalarNonlinearFunction functions.
source
MathOptInterface.FileFormats.MPS.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.MPS.Model.

Keyword arguments are:

  • warn::Bool=false: print a warning when variables or constraints are renamed.
  • print_objsense::Bool=false: print the OBJSENSE section when writing
  • generic_names::Bool=false: strip all names in the model and replace them with the generic names C$i and R$i for the i'th column and row respectively.
  • quadratic_format::QuadraticFormat = kQuadraticFormatGurobi: specify the solver-specific extension used when writing the quadratic components of the model. Options are kQuadraticFormatGurobi, kQuadraticFormatCPLEX, and kQuadraticFormatMosek.
source
MathOptInterface.FileFormats.SDPA.ModelType
Model(; number_type::Type = Float64)

Create an empty instance of FileFormats.SDPA.Model{number_type}.

It is important to be aware that the SDPA file format is interpreted in geometric form and not standard conic form. The standard conic form and geometric conic form are two dual standard forms for semidefinite programs (SDPs). The geometric conic form of an SDP is as follows:

\[\begin{align} +)

Return model corresponding to the FileFormat format, or, if format == FORMAT_AUTOMATIC, guess the format from filename.

The filename argument is only needed if format == FORMAT_AUTOMATIC.

kwargs are passed to the underlying model constructor.

source
MathOptInterface.FileFormats.FileFormatType
FileFormat

List of accepted export formats.

  • FORMAT_AUTOMATIC: try to detect the file format based on the file name
  • FORMAT_CBF: the Conic Benchmark format
  • FORMAT_LP: the LP file format
  • FORMAT_MOF: the MathOptFormat file format
  • FORMAT_MPS: the MPS file format
  • FORMAT_NL: the AMPL .nl file format
  • FORMAT_REW: the .rew file format, which is MPS with generic names
  • FORMAT_SDPA: the SemiDefinite Programming Algorithm format
source
MathOptInterface.FileFormats.LP.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.LP.Model.

Keyword arguments are:

  • maximum_length::Int=255: the maximum length for the name of a variable. lp_solve 5.0 allows only 16 characters, while CPLEX 12.5+ allow 255.

  • warn::Bool=false: print a warning when variables or constraints are renamed.

source
MathOptInterface.FileFormats.MOF.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.MOF.Model.

Keyword arguments are:

  • print_compact::Bool=false: print the JSON file in a compact format without spaces or newlines.
  • warn::Bool=false: print a warning when variables or constraints are renamed
  • differentiation_backend::MOI.Nonlinear.AbstractAutomaticDifferentiation = MOI.Nonlinear.SparseReverseMode(): automatic differentiation backend to use when reading models with nonlinear constraints and objectives.
  • use_nlp_block::Bool=true: if true parse "ScalarNonlinearFunction" into an MOI.NLPBlock. If false, "ScalarNonlinearFunction" are parsed as MOI.ScalarNonlinearFunction functions.
source
MathOptInterface.FileFormats.MPS.ModelType
Model(; kwargs...)

Create an empty instance of FileFormats.MPS.Model.

Keyword arguments are:

  • warn::Bool=false: print a warning when variables or constraints are renamed.
  • print_objsense::Bool=false: print the OBJSENSE section when writing
  • generic_names::Bool=false: strip all names in the model and replace them with the generic names C$i and R$i for the i'th column and row respectively.
  • quadratic_format::QuadraticFormat = kQuadraticFormatGurobi: specify the solver-specific extension used when writing the quadratic components of the model. Options are kQuadraticFormatGurobi, kQuadraticFormatCPLEX, and kQuadraticFormatMosek.
source
MathOptInterface.FileFormats.SDPA.ModelType
Model(; number_type::Type = Float64)

Create an empty instance of FileFormats.SDPA.Model{number_type}.

It is important to be aware that the SDPA file format is interpreted in geometric form and not standard conic form. The standard conic form and geometric conic form are two dual standard forms for semidefinite programs (SDPs). The geometric conic form of an SDP is as follows:

\[\begin{align} & \min_{y \in \mathbb{R}^m} & b^T y \\ & \;\;\text{s.t.} & \sum_{i=1}^m A_i y_i - C & \in \mathbb{K} @@ -12,14 +12,14 @@ & \max_{X \in \mathbb{K}} & \text{tr}(CX) \\ & \;\;\text{s.t.} & \text{tr}(A_iX) & = b_i & i = 1, \ldots, m. -\end{align}\]

In other words, the standard conic form contains nonnegative and positive semidefinite variables with equality constraints. That is, in the MathOptInterface's terminology, MOI.VectorOfVariables-in-MOI.Nonnegatives, MOI.VectorOfVariables-in-MOI.PositiveSemidefiniteConeTriangle and MOI.ScalarAffineFunction-in-MOI.EqualTo constraints.

If a model is in standard conic form, use Dualization.jl to transform it into the geometric conic form before writting it. Otherwise, the nonnegative (resp. positive semidefinite) variables will be bridged into free variables with affine constraints constraining them to belong to the nonnegative orthant (resp. positive semidefinite cone) by the MOI.Bridges.Constraint.VectorFunctionizeBridge. Moreover, equality constraints will be bridged into pairs of affine constraints in the nonnegative orthant by the MOI.Bridges.Constraint.SplitIntervalBridge and then the MOI.Bridges.Constraint.VectorizeBridge.

If a solver is in standard conic form, use Dualization.jl to transform the model read into standard conic form before copying it to the solver. Otherwise, the free variables will be bridged into pairs of variables in the nonnegative orthant by the MOI.Bridges.Variable.FreeBridge and affine constraints will be bridged into equality constraints by creating a slack variable by the MOI.Bridges.Constraint.VectorSlackBridge.

source

Other helpers

MathOptInterface.FileFormats.NL.SolFileResultsType
SolFileResults(
+\end{align}\]

In other words, the standard conic form contains nonnegative and positive semidefinite variables with equality constraints. That is, in the MathOptInterface's terminology, MOI.VectorOfVariables-in-MOI.Nonnegatives, MOI.VectorOfVariables-in-MOI.PositiveSemidefiniteConeTriangle and MOI.ScalarAffineFunction-in-MOI.EqualTo constraints.

If a model is in standard conic form, use Dualization.jl to transform it into the geometric conic form before writting it. Otherwise, the nonnegative (resp. positive semidefinite) variables will be bridged into free variables with affine constraints constraining them to belong to the nonnegative orthant (resp. positive semidefinite cone) by the MOI.Bridges.Constraint.VectorFunctionizeBridge. Moreover, equality constraints will be bridged into pairs of affine constraints in the nonnegative orthant by the MOI.Bridges.Constraint.SplitIntervalBridge and then the MOI.Bridges.Constraint.VectorizeBridge.

If a solver is in standard conic form, use Dualization.jl to transform the model read into standard conic form before copying it to the solver. Otherwise, the free variables will be bridged into pairs of variables in the nonnegative orthant by the MOI.Bridges.Variable.FreeBridge and affine constraints will be bridged into equality constraints by creating a slack variable by the MOI.Bridges.Constraint.VectorSlackBridge.

source

Other helpers

MathOptInterface.FileFormats.NL.SolFileResultsType
SolFileResults(
     filename::String,
     model::Model;
     suffix_lower_bound_duals::Vector{String} =
         ["ipopt_zL_out", "lower_bound_duals"],
     suffix_uuper_bound_duals::Vector{String} =
         ["ipopt_zU_out", "upper_bound_duals"],
-)

Parse the .sol file filename created by solving model and return a SolFileResults struct.

The returned struct supports the MOI.get API for querying result attributes such as MOI.TerminationStatus, MOI.VariablePrimal, and MOI.ConstraintDual.

source
SolFileResults(
+)

Parse the .sol file filename created by solving model and return a SolFileResults struct.

The returned struct supports the MOI.get API for querying result attributes such as MOI.TerminationStatus, MOI.VariablePrimal, and MOI.ConstraintDual.

source
SolFileResults(
     raw_status::String,
     termination_status::MOI.TerminationStatusCode,
-)

Return a SolFileResults struct with MOI.RawStatusString set to raw_status, MOI.TerminationStatus set to termination_status, and MOI.PrimalStatus and MOI.DualStatus set to NO_SOLUTION.

All other attributes are un-set.

source
+)

Return a SolFileResults struct with MOI.RawStatusString set to raw_status, MOI.TerminationStatus set to termination_status, and MOI.PrimalStatus and MOI.DualStatus set to NO_SOLUTION.

All other attributes are un-set.

source diff --git a/dev/submodules/Nonlinear/overview/index.html b/dev/submodules/Nonlinear/overview/index.html index 1040b393c5..c3623f4e1f 100644 --- a/dev/submodules/Nonlinear/overview/index.html +++ b/dev/submodules/Nonlinear/overview/index.html @@ -180,4 +180,4 @@ Node(NODE_VARIABLE, 1, 1), ], [2.0], - );

The ordering of the nodes in the tape must satisfy two rules:

  • The children of a node must appear after the parent. This means that the tape is ordered topologically, so that a reverse pass of the nodes evaluates all children nodes before their parent
  • The arguments for a CALL node are ordered in the tape based on the order in which they appear in the function call.

Design goals

This is less readable than the other options, but does this data structure meet our design goals?

Instead of a heap-allocated object for each node, we only have two Vectors for each expression, nodes and values, as well as two constant vectors for the OPERATORS. In addition, all fields are concretely typed, and there are no Union or Any types.

For our third goal, it is not easy to identify the children of a node, but it is easy to identify the parent of any node. Therefore, we can use Nonlinear.adjacency_matrix to compute a sparse matrix that maps parents to their children.

The design in practice

In practice, Node and Expression are exactly Nonlinear.Node and Nonlinear.Expression. However, Nonlinear.NodeType has more fields to account for comparison operators such as :>= and :<=, logic operators such as :&& and :||, nonlinear parameters, and nested subexpressions.

Moreover, instead of storing the operators as global constants, they are stored in Nonlinear.OperatorRegistry, and it also stores a vector of logic operators and a vector of comparison operators. In addition to Nonlinear.DEFAULT_UNIVARIATE_OPERATORS and Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS, you can register user-defined functions using Nonlinear.register_operator.

Nonlinear.Model is a struct that stores the Nonlinear.OperatorRegistry, as well as a list of parameters and subexpressions in the model.

ReverseAD

Nonlinear.ReverseAD is a submodule for computing derivatives of a nonlinear optimization problem using sparse reverse-mode automatic differentiation (AD).

This section does not attempt to explain how sparse reverse-mode AD works, but instead explains why MOI contains its own implementation, and highlights notable differences from similar packages.

Warning

Don't use the API in ReverseAD to compute derivatives. Instead, create a Nonlinear.Evaluator object with Nonlinear.SparseReverseMode as the backend, and then query the MOI API methods.

Design goals

The JuliaDiff organization maintains a list of packages for doing AD in Julia. At last count, there were at least ten packages——not including ReverseAD——for reverse-mode AD in Julia. ReverseAD exists because it has a different set of design goals.

  • Goal: handle scale and sparsity. The types of nonlinear optimization problems that MOI represents can be large scale (10^5 or more functions across 10^5 or more variables) with very sparse derivatives. The ability to compute a sparse Hessian matrix is essential. To the best of our knowledge, ReverseAD is the only reverse-mode AD system in Julia that handles sparsity by default.
  • Goal: limit the scope to improve robustness. Most other AD packages accept arbitrary Julia functions as input and then trace an expression graph using operator overloading. This means they must deal (or detect and ignore) with control flow, I/O, and other vagaries of Julia. In contrast, ReverseAD only accepts functions in the form of Nonlinear.Expression, which greatly limits the range of syntax that it must deal with. By reducing the scope of what we accept as input to functions relevant for mathematical optimization, we can provide a simpler implementation with various performance optimizations.
  • Goal: provide outputs which match what solvers expect. Other AD packages focus on differentiating individual Julia functions. In contrast, ReverseAD has a very specific use-case: to generate outputs needed by the MOI nonlinear API. This means it needs to efficiently compute sparse Hessians, and it needs subexpression handling to avoid recomputing subexpressions that are shared between functions.

History

ReverseAD started life as ReverseDiffSparse.jl, development of which began in early 2014(!). This was well before the other AD packages started development. Because we had a well-tested, working AD in JuMP, there was less motivation to contribute to and explore other AD packages. The lack of historical interaction also meant that other packages were not optimized for the types of problems that JuMP is built for (that is, large-scale sparse problems). When we first created MathOptInterface, we kept the AD in JuMP to simplify the transition, and post-poned the development of a first-class nonlinear interface in MathOptInterface.

Prior to the introduction of Nonlinear, JuMP's nonlinear implementation was a confusing mix of functions and types spread across the code base and in the private _Derivatives submodule. This made it hard to swap the AD system for another. The main motivation for refactoring JuMP to create the Nonlinear submodule in MathOptInterface was to abstract the interface between JuMP and the AD system, allowing us to swap-in and test new AD systems in the future.

+ );

The ordering of the nodes in the tape must satisfy two rules:

  • The children of a node must appear after the parent. This means that the tape is ordered topologically, so that a reverse pass of the nodes evaluates all children nodes before their parent
  • The arguments for a CALL node are ordered in the tape based on the order in which they appear in the function call.

Design goals

This is less readable than the other options, but does this data structure meet our design goals?

Instead of a heap-allocated object for each node, we only have two Vectors for each expression, nodes and values, as well as two constant vectors for the OPERATORS. In addition, all fields are concretely typed, and there are no Union or Any types.

For our third goal, it is not easy to identify the children of a node, but it is easy to identify the parent of any node. Therefore, we can use Nonlinear.adjacency_matrix to compute a sparse matrix that maps parents to their children.

The design in practice

In practice, Node and Expression are exactly Nonlinear.Node and Nonlinear.Expression. However, Nonlinear.NodeType has more fields to account for comparison operators such as :>= and :<=, logic operators such as :&& and :||, nonlinear parameters, and nested subexpressions.

Moreover, instead of storing the operators as global constants, they are stored in Nonlinear.OperatorRegistry, and it also stores a vector of logic operators and a vector of comparison operators. In addition to Nonlinear.DEFAULT_UNIVARIATE_OPERATORS and Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS, you can register user-defined functions using Nonlinear.register_operator.

Nonlinear.Model is a struct that stores the Nonlinear.OperatorRegistry, as well as a list of parameters and subexpressions in the model.

ReverseAD

Nonlinear.ReverseAD is a submodule for computing derivatives of a nonlinear optimization problem using sparse reverse-mode automatic differentiation (AD).

This section does not attempt to explain how sparse reverse-mode AD works, but instead explains why MOI contains its own implementation, and highlights notable differences from similar packages.

Warning

Don't use the API in ReverseAD to compute derivatives. Instead, create a Nonlinear.Evaluator object with Nonlinear.SparseReverseMode as the backend, and then query the MOI API methods.

Design goals

The JuliaDiff organization maintains a list of packages for doing AD in Julia. At last count, there were at least ten packages——not including ReverseAD——for reverse-mode AD in Julia. ReverseAD exists because it has a different set of design goals.

  • Goal: handle scale and sparsity. The types of nonlinear optimization problems that MOI represents can be large scale (10^5 or more functions across 10^5 or more variables) with very sparse derivatives. The ability to compute a sparse Hessian matrix is essential. To the best of our knowledge, ReverseAD is the only reverse-mode AD system in Julia that handles sparsity by default.
  • Goal: limit the scope to improve robustness. Most other AD packages accept arbitrary Julia functions as input and then trace an expression graph using operator overloading. This means they must deal (or detect and ignore) with control flow, I/O, and other vagaries of Julia. In contrast, ReverseAD only accepts functions in the form of Nonlinear.Expression, which greatly limits the range of syntax that it must deal with. By reducing the scope of what we accept as input to functions relevant for mathematical optimization, we can provide a simpler implementation with various performance optimizations.
  • Goal: provide outputs which match what solvers expect. Other AD packages focus on differentiating individual Julia functions. In contrast, ReverseAD has a very specific use-case: to generate outputs needed by the MOI nonlinear API. This means it needs to efficiently compute sparse Hessians, and it needs subexpression handling to avoid recomputing subexpressions that are shared between functions.

History

ReverseAD started life as ReverseDiffSparse.jl, development of which began in early 2014(!). This was well before the other AD packages started development. Because we had a well-tested, working AD in JuMP, there was less motivation to contribute to and explore other AD packages. The lack of historical interaction also meant that other packages were not optimized for the types of problems that JuMP is built for (that is, large-scale sparse problems). When we first created MathOptInterface, we kept the AD in JuMP to simplify the transition, and post-poned the development of a first-class nonlinear interface in MathOptInterface.

Prior to the introduction of Nonlinear, JuMP's nonlinear implementation was a confusing mix of functions and types spread across the code base and in the private _Derivatives submodule. This made it hard to swap the AD system for another. The main motivation for refactoring JuMP to create the Nonlinear submodule in MathOptInterface was to abstract the interface between JuMP and the AD system, allowing us to swap-in and test new AD systems in the future.

diff --git a/dev/submodules/Nonlinear/reference/index.html b/dev/submodules/Nonlinear/reference/index.html index 161d8591da..ba8fbf944a 100644 --- a/dev/submodules/Nonlinear/reference/index.html +++ b/dev/submodules/Nonlinear/reference/index.html @@ -1,5 +1,5 @@ -API Reference · MathOptInterface

Nonlinear Modeling

More information can be found in the Nonlinear section of the manual.

MathOptInterface.NonlinearModule
Nonlinear
Warning

The Nonlinear submodule is experimental. Until this message is removed, breaking changes may be introduced in any minor or patch release of MathOptInterface.

source
MathOptInterface.Nonlinear.ModelType
Model()

The core datastructure for representing a nonlinear optimization problem.

It has the following fields:

  • objective::Union{Nothing,Expression} : holds the nonlinear objective function, if one exists, otherwise nothing.
  • expressions::Vector{Expression} : a vector of expressions in the model.
  • constraints::OrderedDict{ConstraintIndex,Constraint} : a map from ConstraintIndex to the corresponding Constraint. An OrderedDict is used instead of a Vector to support constraint deletion.
  • parameters::Vector{Float64} : holds the current values of the parameters.
  • operators::OperatorRegistry : stores the operators used in the model.
source

Expressions

MathOptInterface.Nonlinear.add_expressionFunction
add_expression(model::Model, expr)::ExpressionIndex

Parse expr into a Expression and add to model. Returns an ExpressionIndex that can be interpolated into other input expressions.

expr must be a type that is supported by parse_expression.

Example

julia> import MathOptInterface as MOI
+API Reference · MathOptInterface

Nonlinear Modeling

More information can be found in the Nonlinear section of the manual.

MathOptInterface.NonlinearModule
Nonlinear
Warning

The Nonlinear submodule is experimental. Until this message is removed, breaking changes may be introduced in any minor or patch release of MathOptInterface.

source
MathOptInterface.Nonlinear.ModelType
Model()

The core datastructure for representing a nonlinear optimization problem.

It has the following fields:

  • objective::Union{Nothing,Expression} : holds the nonlinear objective function, if one exists, otherwise nothing.
  • expressions::Vector{Expression} : a vector of expressions in the model.
  • constraints::OrderedDict{ConstraintIndex,Constraint} : a map from ConstraintIndex to the corresponding Constraint. An OrderedDict is used instead of a Vector to support constraint deletion.
  • parameters::Vector{Float64} : holds the current values of the parameters.
  • operators::OperatorRegistry : stores the operators used in the model.
source

Expressions

MathOptInterface.Nonlinear.add_expressionFunction
add_expression(model::Model, expr)::ExpressionIndex

Parse expr into a Expression and add to model. Returns an ExpressionIndex that can be interpolated into other input expressions.

expr must be a type that is supported by parse_expression.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Nonlinear.Model();
 
@@ -8,7 +8,7 @@
 julia> ex = MOI.Nonlinear.add_expression(model, :($x^2 + 1))
 MathOptInterface.Nonlinear.ExpressionIndex(1)
 
-julia> MOI.Nonlinear.set_objective(model, :(sqrt($ex)))
source

Parameters

MathOptInterface.Nonlinear.add_parameterFunction
add_parameter(model::Model, value::Float64)::ParameterIndex

Add a new parameter to model with the default value value. Returns a ParameterIndex that can be interpolated into other input expressions and used to modify the value of the parameter.

Example

julia> import MathOptInterface as MOI
+julia> MOI.Nonlinear.set_objective(model, :(sqrt($ex)))
source

Parameters

MathOptInterface.Nonlinear.add_parameterFunction
add_parameter(model::Model, value::Float64)::ParameterIndex

Add a new parameter to model with the default value value. Returns a ParameterIndex that can be interpolated into other input expressions and used to modify the value of the parameter.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Nonlinear.Model()
 A Nonlinear.Model with:
@@ -24,7 +24,7 @@
 MathOptInterface.Nonlinear.ParameterIndex(1)
 
 julia> c = MOI.Nonlinear.add_constraint(model, :($x^2 - $p), MOI.LessThan(0.0))
-MathOptInterface.Nonlinear.ConstraintIndex(1)
source

Objectives

Objectives

MathOptInterface.Nonlinear.set_objectiveFunction
set_objective(model::Model, obj)::Nothing

Parse obj into a Expression and set as the objective function of model.

obj must be a type that is supported by parse_expression.

To remove the objective, pass nothing.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Nonlinear.Model()
 A Nonlinear.Model with:
@@ -40,7 +40,7 @@
 
 julia> MOI.Nonlinear.set_objective(model, x)
 
-julia> MOI.Nonlinear.set_objective(model, nothing)
source

Constraints

Constraints

MathOptInterface.Nonlinear.add_constraintFunction
add_constraint(
     model::Model,
     func,
     set::Union{
@@ -56,7 +56,7 @@
 julia> x = MOI.VariableIndex(1);
 
 julia> c = MOI.Nonlinear.add_constraint(model, :($x^2), MOI.LessThan(1.0))
-MathOptInterface.Nonlinear.ConstraintIndex(1)
source
MathOptInterface.Nonlinear.deleteFunction
delete(model::Model, c::ConstraintIndex)::Nothing

Delete the constraint index c from model.

Example

julia> import MathOptInterface as MOI
+MathOptInterface.Nonlinear.ConstraintIndex(1)
source
MathOptInterface.Nonlinear.deleteFunction
delete(model::Model, c::ConstraintIndex)::Nothing

Delete the constraint index c from model.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Nonlinear.Model()
 A Nonlinear.Model with:
@@ -85,7 +85,7 @@
  0 objectives
  0 parameters
  0 expressions
- 0 constraints
source

User-defined operators

User-defined operators

MathOptInterface.Nonlinear.DEFAULT_UNIVARIATE_OPERATORSConstant
DEFAULT_UNIVARIATE_OPERATORS

The list of univariate operators that are supported by default.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.Nonlinear.DEFAULT_UNIVARIATE_OPERATORS
 73-element Vector{Symbol}:
@@ -108,7 +108,7 @@
  :bessely0
  :bessely1
  :erfcx
- :dawson
source
MathOptInterface.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORSConstant
DEFAULT_MULTIVARIATE_OPERATORS

The list of multivariate operators that are supported by default.

Example

julia> import MathOptInterface as MOI
 
 julia> MOI.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS
 9-element Vector{Symbol}:
@@ -120,66 +120,66 @@
  :ifelse
  :atan
  :min
- :max
source
MathOptInterface.Nonlinear.register_operatorFunction
register_operator(
     model::Model,
     op::Symbol,
     nargs::Int,
     f::Function,
     [∇f::Function],
     [∇²f::Function],
-)

Register the user-defined operator op with nargs input arguments in model.

Univariate functions

  • f(x::T)::T must be a function that takes a single input argument x and returns the function evaluated at x. If ∇f and ∇²f are not provided, f must support any Real input type T.
  • ∇f(x::T)::T is a function that takes a single input argument x and returns the first derivative of f with respect to x. If ∇²f is not provided, ∇f must support any Real input type T.
  • ∇²f(x::T)::T is a function that takes a single input argument x and returns the second derivative of f with respect to x.

Multivariate functions

  • f(x::T...)::T must be a function that takes a nargs input arguments x and returns the function evaluated at x. If ∇f and ∇²f are not provided, f must support any Real input type T.
  • ∇f(g::AbstractVector{T}, x::T...)::T is a function that takes a cache vector g of length length(x), and fills each element g[i] with the partial derivative of f with respect to x[i].
  • ∇²f(H::AbstractMatrix, x::T...)::T is a function that takes a matrix H and fills the lower-triangular components H[i, j] with the Hessian of f with respect to x[i] and x[j] for i >= j.

Notes for multivariate Hessians

  • H has size(H) == (length(x), length(x)), but you must not access elements H[i, j] for i > j.
  • H is dense, but you do not need to fill structural zeros.
source
MathOptInterface.Nonlinear.register_operator_if_neededFunction
register_operator_if_needed(
+)

Register the user-defined operator op with nargs input arguments in model.

Univariate functions

  • f(x::T)::T must be a function that takes a single input argument x and returns the function evaluated at x. If ∇f and ∇²f are not provided, f must support any Real input type T.
  • ∇f(x::T)::T is a function that takes a single input argument x and returns the first derivative of f with respect to x. If ∇²f is not provided, ∇f must support any Real input type T.
  • ∇²f(x::T)::T is a function that takes a single input argument x and returns the second derivative of f with respect to x.

Multivariate functions

  • f(x::T...)::T must be a function that takes a nargs input arguments x and returns the function evaluated at x. If ∇f and ∇²f are not provided, f must support any Real input type T.
  • ∇f(g::AbstractVector{T}, x::T...)::T is a function that takes a cache vector g of length length(x), and fills each element g[i] with the partial derivative of f with respect to x[i].
  • ∇²f(H::AbstractMatrix, x::T...)::T is a function that takes a matrix H and fills the lower-triangular components H[i, j] with the Hessian of f with respect to x[i] and x[j] for i >= j.

Notes for multivariate Hessians

  • H has size(H) == (length(x), length(x)), but you must not access elements H[i, j] for i > j.
  • H is dense, but you do not need to fill structural zeros.
source
MathOptInterface.Nonlinear.eval_multivariate_hessianFunction
eval_multivariate_hessian(
     registry::OperatorRegistry,
     op::Symbol,
     H::AbstractMatrix,
     x::AbstractVector{T},
-) where {T}

Evaluate the Hessian of operator ∇²op(x), where op is a multivariate function in registry.

The Hessian is stored in the lower-triangular part of the matrix H.

Note

Implementations of the Hessian operators will not fill structural zeros. Therefore, before calling this function you should pre-populate the matrix H with 0.

source
MathOptInterface.Nonlinear.eval_logic_functionFunction
eval_logic_function(
+) where {T}

Evaluate the Hessian of operator ∇²op(x), where op is a multivariate function in registry.

The Hessian is stored in the lower-triangular part of the matrix H.

Note

Implementations of the Hessian operators will not fill structural zeros. Therefore, before calling this function you should pre-populate the matrix H with 0.

source

Automatic-differentiation backends

Automatic-differentiation backends

MathOptInterface.Nonlinear.EvaluatorType
Evaluator(
     model::Model,
     backend::AbstractAutomaticDifferentiation,
     ordered_variables::Vector{MOI.VariableIndex},
-)

Create Evaluator, a subtype of MOI.AbstractNLPEvaluator, from Model.

source
MathOptInterface.Nonlinear.SparseReverseModeType
SparseReverseMode() <: AbstractAutomaticDifferentiation

An implementation of AbstractAutomaticDifferentiation that uses sparse reverse-mode automatic differentiation to compute derivatives. Supports all features in the MOI nonlinear interface.

source

Data-structure

MathOptInterface.Nonlinear.SparseReverseModeType
SparseReverseMode() <: AbstractAutomaticDifferentiation

An implementation of AbstractAutomaticDifferentiation that uses sparse reverse-mode automatic differentiation to compute derivatives. Supports all features in the MOI nonlinear interface.

source

Data-structure

MathOptInterface.Nonlinear.NodeType
struct Node
     type::NodeType
     index::Int
     parent::Int
-end

A single node in a nonlinear expression tree. Used by Expression.

See the MathOptInterface documentation for information on how the nodes and values form an expression tree.

source
MathOptInterface.Nonlinear.NodeTypeType
NodeType

An enum describing the possible node types. Each Node has a .index field, which should be interpreted as follows:

  • NODE_CALL_MULTIVARIATE: the index into operators.multivariate_operators
  • NODE_CALL_UNIVARIATE: the index into operators.univariate_operators
  • NODE_LOGIC: the index into operators.logic_operators
  • NODE_COMPARISON: the index into operators.comparison_operators
  • NODE_MOI_VARIABLE: the value of MOI.VariableIndex(index) in the user's space of the model.
  • NODE_VARIABLE: the 1-based index of the internal vector
  • NODE_VALUE: the index into the .values field of Expression
  • NODE_PARAMETER: the index into data.parameters
  • NODE_SUBEXPRESSION: the index into data.expressions
source
MathOptInterface.Nonlinear.NodeTypeType
NodeType

An enum describing the possible node types. Each Node has a .index field, which should be interpreted as follows:

  • NODE_CALL_MULTIVARIATE: the index into operators.multivariate_operators
  • NODE_CALL_UNIVARIATE: the index into operators.univariate_operators
  • NODE_LOGIC: the index into operators.logic_operators
  • NODE_COMPARISON: the index into operators.comparison_operators
  • NODE_MOI_VARIABLE: the value of MOI.VariableIndex(index) in the user's space of the model.
  • NODE_VARIABLE: the 1-based index of the internal vector
  • NODE_VALUE: the index into the .values field of Expression
  • NODE_PARAMETER: the index into data.parameters
  • NODE_SUBEXPRESSION: the index into data.expressions
source
MathOptInterface.Nonlinear.ExpressionType
struct Expression
     nodes::Vector{Node}
     values::Vector{Float64}
-end

The core type that represents a nonlinear expression. See the MathOptInterface documentation for information on how the nodes and values form an expression tree.

source
MathOptInterface.Nonlinear.ConstraintType
struct Constraint
+end

The core type that represents a nonlinear expression. See the MathOptInterface documentation for information on how the nodes and values form an expression tree.

source
MathOptInterface.Nonlinear.ConstraintType
struct Constraint
     expression::Expression
     set::Union{
         MOI.LessThan{Float64},
@@ -187,16 +187,16 @@
         MOI.EqualTo{Float64},
         MOI.Interval{Float64},
     }
-end

A type to hold information relating to the nonlinear constraint f(x) in S, where f(x) is defined by .expression, and S is .set.

source
MathOptInterface.Nonlinear.adjacency_matrixFunction
adjacency_matrix(nodes::Vector{Node})

Compute the sparse adjacency matrix describing the parent-child relationships in nodes.

The element (i, j) is true if there is an edge from node[j] to node[i]. Since we get a column-oriented matrix, this gives us a fast way to look up the edges leaving any node (that is, the children).

source
MathOptInterface.Nonlinear.adjacency_matrixFunction
adjacency_matrix(nodes::Vector{Node})

Compute the sparse adjacency matrix describing the parent-child relationships in nodes.

The element (i, j) is true if there is an edge from node[j] to node[i]. Since we get a column-oriented matrix, this gives us a fast way to look up the edges leaving any node (that is, the children).

source
MathOptInterface.Nonlinear.parse_expressionFunction
parse_expression(data::Model, input)::Expression

Parse input into a Expression.

source
parse_expression(
     data::Model,
     expr::Expression,
     input::Any,
     parent_index::Int,
-)::Expression

Parse input into a Expression, and add it to expr as a child of expr.nodes[parent_index]. Existing subexpressions and parameters are stored in data.

You can extend parsing support to new types of objects by overloading this method with a different type on input::Any.

source
MathOptInterface.Nonlinear.convert_to_exprFunction
convert_to_expr(data::Model, expr::Expression)

Convert the Expression expr into a Julia Expr.

source
convert_to_expr(
+)::Expression

Parse input into a Expression, and add it to expr as a child of expr.nodes[parent_index]. Existing subexpressions and parameters are stored in data.

You can extend parsing support to new types of objects by overloading this method with a different type on input::Any.

source
MathOptInterface.Nonlinear.convert_to_exprFunction
convert_to_expr(data::Model, expr::Expression)

Convert the Expression expr into a Julia Expr.

source
convert_to_expr(
     evaluator::Evaluator,
     expr::Expression;
     moi_output_format::Bool,
-)

Convert the Expression expr into a Julia Expr.

If moi_output_format = true:

  • subexpressions will be converted to Julia Expr and substituted into the output expression.
  • the current value of each parameter will be interpolated into the expression
  • variables will be represented in the form x[MOI.VariableIndex(i)]

If moi_output_format = false:

Warning

To use moi_output_format = true, you must have first called MOI.initialize with :ExprGraph as a requested feature.

source
MathOptInterface.Nonlinear.ordinal_indexFunction
ordinal_index(evaluator::Evaluator, c::ConstraintIndex)::Int

Return the 1-indexed value of the constraint index c in evaluator.

Example

julia> import MathOptInterface as MOI
+)

Convert the Expression expr into a Julia Expr.

If moi_output_format = true:

  • subexpressions will be converted to Julia Expr and substituted into the output expression.
  • the current value of each parameter will be interpolated into the expression
  • variables will be represented in the form x[MOI.VariableIndex(i)]

If moi_output_format = false:

Warning

To use moi_output_format = true, you must have first called MOI.initialize with :ExprGraph as a requested feature.

source
MathOptInterface.Nonlinear.ordinal_indexFunction
ordinal_index(evaluator::Evaluator, c::ConstraintIndex)::Int

Return the 1-indexed value of the constraint index c in evaluator.

Example

julia> import MathOptInterface as MOI
 
 julia> model = MOI.Nonlinear.Model()
 A Nonlinear.Model with:
@@ -232,4 +232,4 @@
 julia> MOI.initialize(evaluator, Symbol[])
 
 julia> MOI.Nonlinear.ordinal_index(evaluator, c2)  # Returns 1
-1
source
+1
source
diff --git a/dev/submodules/Test/overview/index.html b/dev/submodules/Test/overview/index.html index 2937d2d593..a1f652aebb 100644 --- a/dev/submodules/Test/overview/index.html +++ b/dev/submodules/Test/overview/index.html @@ -163,4 +163,4 @@ ), ) return -end

Finally, you also need to implement Test.version_added. If we added this test when the latest released version of MOI was v0.10.5, define:

version_added(::typeof(test_unit_optimize!_twice)) = v"0.10.6"

Step 6

Commit the changes to git from ~/.julia/dev/MathOptInterface and submit the PR for review.

Tip

If you need help writing a test, open an issue on GitHub, or ask the Developer Chatroom.

+end

Finally, you also need to implement Test.version_added. If we added this test when the latest released version of MOI was v0.10.5, define:

version_added(::typeof(test_unit_optimize!_twice)) = v"0.10.6"

Step 6

Commit the changes to git from ~/.julia/dev/MathOptInterface and submit the PR for review.

Tip

If you need help writing a test, open an issue on GitHub, or ask the Developer Chatroom.

diff --git a/dev/submodules/Test/reference/index.html b/dev/submodules/Test/reference/index.html index 5ce1934a29..f4acccd159 100644 --- a/dev/submodules/Test/reference/index.html +++ b/dev/submodules/Test/reference/index.html @@ -17,7 +17,7 @@ MOI.ConstraintName, MOI.delete, ], - );source
MathOptInterface.Test.runtestsFunction
runtests(
     model::MOI.ModelLike,
     config::Config;
     include::Vector{Union{String,Regex}} = String[],
@@ -34,7 +34,7 @@
     warn_unsupported = true,
     verbose = true,
     exclude_tests_after = v"0.10.5",
-)
source
MathOptInterface.Test.setup_testFunction
setup_test(::typeof(f), model::MOI.ModelLike, config::Config)

Overload this method to modify model before running the test function f on model with config. You can also modify the fields in config (for example, to loosen the default tolerances).

This function should either return nothing, or return a function which, when called with zero arguments, undoes the setup to return the model to its previous state. You do not need to undo any modifications to config.

This function is most useful when writing new tests of the tests for MOI, but it can also be used to set test-specific tolerances, etc.

See also: runtests

Example

function MOI.Test.setup_test(
+)
source
MathOptInterface.Test.setup_testFunction
setup_test(::typeof(f), model::MOI.ModelLike, config::Config)

Overload this method to modify model before running the test function f on model with config. You can also modify the fields in config (for example, to loosen the default tolerances).

This function should either return nothing, or return a function which, when called with zero arguments, undoes the setup to return the model to its previous state. You do not need to undo any modifications to config.

This function is most useful when writing new tests of the tests for MOI, but it can also be used to set test-specific tolerances, etc.

See also: runtests

Example

function MOI.Test.setup_test(
     ::typeof(MOI.Test.test_linear_VariablePrimalStart_partial),
     mock::MOIU.MockOptimizer,
     ::MOI.Test.Config,
@@ -50,8 +50,8 @@
         return
     end
     return reset_function
-end
source
MathOptInterface.Test.version_addedFunction
version_added(::typeof(function_name))

Returns the version of MOI in which the test function_name was added.

This method should be implemented for all new tests.

See the exclude_tests_after keyword of runtests for more details.

source
MathOptInterface.Test.@requiresMacro
@requires(x)

Check that the condition x is true. Otherwise, throw an RequirementUnmet error to indicate that the model does not support something required by the test function.

Example

@requires MOI.supports(model, MOI.Silent())
-@test MOI.get(model, MOI.Silent())
source
MathOptInterface.Test.version_addedFunction
version_added(::typeof(function_name))

Returns the version of MOI in which the test function_name was added.

This method should be implemented for all new tests.

See the exclude_tests_after keyword of runtests for more details.

source
MathOptInterface.Test.@requiresMacro
@requires(x)

Check that the condition x is true. Otherwise, throw an RequirementUnmet error to indicate that the model does not support something required by the test function.

Example

@requires MOI.supports(model, MOI.Silent())
+@test MOI.get(model, MOI.Silent())
source
MathOptInterface.Test.HS071Type
HS071(
     enable_hessian::Bool,
     enable_hessian_vector_product::Bool = false,
 )

An MOI.AbstractNLPEvaluator for the problem:

\[\begin{aligned} @@ -59,4 +59,4 @@ \text{subject to}\ & x_1 * x_2 * x_3 * x_4 \ge 25 \\ & x_1^2 + x_2^2 + x_3^2 + x_4^2 = 40 \\ & 1 \le x_1, x_2, x_3, x_4 \le 5 -\end{aligned}\]

The optimal solution is [1.000, 4.743, 3.821, 1.379].

source
+\end{aligned}\]

The optimal solution is [1.000, 4.743, 3.821, 1.379].

source diff --git a/dev/submodules/Utilities/overview/index.html b/dev/submodules/Utilities/overview/index.html index 7ab366608c..b8b1ee9cc3 100644 --- a/dev/submodules/Utilities/overview/index.html +++ b/dev/submodules/Utilities/overview/index.html @@ -374,4 +374,4 @@ index_map = MOI.copy_to(dest, src) for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent()) function_barrier(dest, src, index_map[F, S]) -end +end diff --git a/dev/submodules/Utilities/reference/index.html b/dev/submodules/Utilities/reference/index.html index 5bbf11c5b1..7043ca7d8f 100644 --- a/dev/submodules/Utilities/reference/index.html +++ b/dev/submodules/Utilities/reference/index.html @@ -6,7 +6,7 @@ ├ ObjectiveSense: FEASIBILITY_SENSE ├ ObjectiveFunctionType: MOI.ScalarAffineFunction{Float64} ├ NumberOfVariables: 0 -└ NumberOfConstraints: 0source

Utilities.UniversalFallback

MathOptInterface.Utilities.UniversalFallbackType
UniversalFallback

The UniversalFallback can be applied on a MOI.ModelLike model to create the model UniversalFallback(model) supporting any constraint and attribute. This allows to have a specialized implementation in model for performance critical constraints and attributes while still supporting other attributes with a small performance penalty. Note that model is unaware of constraints and attributes stored by UniversalFallback so this is not appropriate if model is an optimizer (for this reason, MOI.optimize! has not been implemented). In that case, optimizer bridges should be used instead.

source

Utilities.@model

Utilities.UniversalFallback

MathOptInterface.Utilities.UniversalFallbackType
UniversalFallback

The UniversalFallback can be applied on a MOI.ModelLike model to create the model UniversalFallback(model) supporting any constraint and attribute. This allows to have a specialized implementation in model for performance critical constraints and attributes while still supporting other attributes with a small performance penalty. Note that model is unaware of constraints and attributes stored by UniversalFallback so this is not appropriate if model is an optimizer (for this reason, MOI.optimize! has not been implemented). In that case, optimizer bridges should be used instead.

source

Utilities.@model

MathOptInterface.Utilities.@modelMacro
macro model(
     model_name,
     scalar_sets,
     typed_scalar_sets,
@@ -28,11 +28,11 @@
     (MOI.VectorOfVariables,),                         # untyped vector functions
     (MOI.VectorAffineFunction,),                      #   typed vector functions
     false,                                            # is_optimizer
-)
source
MathOptInterface.Utilities.GenericModelType
mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T}

Implements a model supporting coefficients of type T and:

  • An objective function stored in .objective::O
  • Variables and VariableIndex constraints stored in .variable_bounds::V
  • F-in-S constraints (excluding VariableIndex constraints) stored in .constraints::C

All interactions take place via the MOI interface, so the types O, V, and C must implement the API as needed for their functionality.

source
MathOptInterface.Utilities.GenericOptimizerType
mutable struct GenericOptimizer{T,O,V,C} <: AbstractOptimizer{T}

Implements a model supporting coefficients of type T and:

  • An objective function stored in .objective::O
  • Variables and VariableIndex constraints stored in .variable_bounds::V
  • F-in-S constraints (excluding VariableIndex constraints) stored in .constraints::C

All interactions take place via the MOI interface, so the types O, V, and C must implement the API as needed for their functionality.

source

.objective

.variables

MathOptInterface.Utilities.GenericModelType
mutable struct GenericModel{T,O,V,C} <: AbstractModelLike{T}

Implements a model supporting coefficients of type T and:

  • An objective function stored in .objective::O
  • Variables and VariableIndex constraints stored in .variable_bounds::V
  • F-in-S constraints (excluding VariableIndex constraints) stored in .constraints::C

All interactions take place via the MOI interface, so the types O, V, and C must implement the API as needed for their functionality.

source
MathOptInterface.Utilities.GenericOptimizerType
mutable struct GenericOptimizer{T,O,V,C} <: AbstractOptimizer{T}

Implements a model supporting coefficients of type T and:

  • An objective function stored in .objective::O
  • Variables and VariableIndex constraints stored in .variable_bounds::V
  • F-in-S constraints (excluding VariableIndex constraints) stored in .constraints::C

All interactions take place via the MOI interface, so the types O, V, and C must implement the API as needed for their functionality.

source

.objective

.variables

MathOptInterface.Utilities.VariablesContainerType
struct VariablesContainer{T} <: AbstractVectorBounds
     set_mask::Vector{UInt16}
     lower::Vector{T}
     upper::Vector{T}
-end

A struct for storing variables and VariableIndex-related constraints. Used in MOI.Utilities.Model by default.

source
MathOptInterface.Utilities.FreeVariablesType
mutable struct FreeVariables <: MOI.ModelLike
     n::Int64
     FreeVariables() = new(0)
 end

A struct for storing free variables that can be used as the variables field of GenericModel or GenericModel. It represents a model that does not support any constraint nor objective function.

Example

The following model type represents a conic model in geometric form. As opposed to VariablesContainer, FreeVariables does not support constraint bounds so they are bridged into an affine constraint in the MOI.Nonnegatives cone as expected for the geometric conic form.

julia> MOI.Utilities.@product_of_sets(
@@ -77,7 +77,7 @@
 MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, MathOptInterface.Nonnegatives}(1)
 
 julia> MOI.Bridges.is_bridged(model, bridge.vector_constraint)
-false
source

.constraints

.constraints

MathOptInterface.Utilities.VectorOfConstraintsType
mutable struct VectorOfConstraints{
     F<:MOI.AbstractFunction,
     S<:MOI.AbstractSet,
 } <: MOI.ModelLike
@@ -87,7 +87,7 @@
         typeof(CleverDicts.key_to_index),
         typeof(CleverDicts.index_to_key),
     }
-end

A struct storing F-in-S constraints as a mapping between the constraint indices to the corresponding tuple of function and set.

source
MathOptInterface.Utilities.@struct_of_constraints_by_function_typesMacro
Utilities.@struct_of_constraints_by_function_types(name, func_types...)

Given a vector of n function types (F1, F2,..., Fn) in func_types, defines a subtype of StructOfConstraints of name name and which type parameters {T, C1, C2, ..., Cn}. It contains n field where the ith field has type Ci and stores the constraints of function type Fi.

The expression Fi can also be a union in which case any constraint for which the function type is in the union is stored in the field with type Ci.

source
MathOptInterface.Utilities.@struct_of_constraints_by_set_typesMacro
Utilities.@struct_of_constraints_by_set_types(name, func_types...)

Given a vector of n set types (S1, S2,..., Sn) in func_types, defines a subtype of StructOfConstraints of name name and which type parameters {T, C1, C2, ..., Cn}. It contains n field where the ith field has type Ci and stores the constraints of set type Si. The expression Si can also be a union in which case any constraint for which the set type is in the union is stored in the field with type Ci. This can be useful if Ci is a MatrixOfConstraints in order to concatenate the coefficients of constraints of several different set types in the same matrix.

source
MathOptInterface.Utilities.struct_of_constraint_codeFunction
struct_of_constraint_code(struct_name, types, field_types = nothing)

Given a vector of n Union{SymbolFun,_UnionSymbolFS{SymbolFun}} or Union{SymbolSet,_UnionSymbolFS{SymbolSet}} in types, defines a subtype of StructOfConstraints of name name and which type parameters {T, F1, F2, ..., Fn} if field_types is nothing and a {T} otherwise. It contains n field where the ith field has type Ci if field_types is nothing and type field_types[i] otherwise. If types is vector of Union{SymbolFun,_UnionSymbolFS{SymbolFun}} (resp. Union{SymbolSet,_UnionSymbolFS{SymbolSet}}) then the constraints of that function (resp. set) type are stored in the corresponding field.

This function is used by the macros @model, @struct_of_constraints_by_function_types and @struct_of_constraints_by_set_types.

source

Caching optimizer

MathOptInterface.Utilities.CachingOptimizerType
CachingOptimizer

CachingOptimizer is an intermediate layer that stores a cache of the model and links it with an optimizer. It supports incremental model construction and modification even when the optimizer doesn't.

Constructors

    CachingOptimizer(cache::MOI.ModelLike, optimizer::AbstractOptimizer)

Creates a CachingOptimizer in AUTOMATIC mode, with the optimizer optimizer.

The type of the optimizer returned is CachingOptimizer{typeof(optimizer), typeof(cache)} so it does not support the function reset_optimizer(::CachingOptimizer, new_optimizer) if the type of new_optimizer is different from the type of optimizer.

    CachingOptimizer(cache::MOI.ModelLike, mode::CachingOptimizerMode)

Creates a CachingOptimizer in the NO_OPTIMIZER state and mode mode.

The type of the optimizer returned is CachingOptimizer{MOI.AbstractOptimizer,typeof(cache)} so it does support the function reset_optimizer(::CachingOptimizer, new_optimizer) if the type of new_optimizer is different from the type of optimizer.

About the type

States

A CachingOptimizer may be in one of three possible states (CachingOptimizerState):

  • NO_OPTIMIZER: The CachingOptimizer does not have any optimizer.
  • EMPTY_OPTIMIZER: The CachingOptimizer has an empty optimizer. The optimizer is not synchronized with the cached model.
  • ATTACHED_OPTIMIZER: The CachingOptimizer has an optimizer, and it is synchronized with the cached model.

Modes

A CachingOptimizer has two modes of operation (CachingOptimizerMode):

  • MANUAL: The only methods that change the state of the CachingOptimizer are Utilities.reset_optimizer, Utilities.drop_optimizer, and Utilities.attach_optimizer. Attempting to perform an operation in the incorrect state results in an error.
  • AUTOMATIC: The CachingOptimizer changes its state when necessary. For example, optimize! will automatically call attach_optimizer (an optimizer must have been previously set). Attempting to add a constraint or perform a modification not supported by the optimizer results in a drop to EMPTY_OPTIMIZER mode.
source
MathOptInterface.Utilities.attach_optimizerFunction
attach_optimizer(model::CachingOptimizer)

Attaches the optimizer to model, copying all model data into it. Can be called only from the EMPTY_OPTIMIZER state. If the copy succeeds, the CachingOptimizer will be in state ATTACHED_OPTIMIZER after the call, otherwise an error is thrown; see MOI.copy_to for more details on which errors can be thrown.

source
MathOptInterface.Utilities.reset_optimizerFunction
reset_optimizer(m::CachingOptimizer, optimizer::MOI.AbstractOptimizer)

Sets or resets m to have the given empty optimizer optimizer.

Can be called from any state. An assertion error will be thrown if optimizer is not empty.

The CachingOptimizer m will be in state EMPTY_OPTIMIZER after the call.

source
reset_optimizer(m::CachingOptimizer)

Detaches and empties the current optimizer. Can be called from ATTACHED_OPTIMIZER or EMPTY_OPTIMIZER state. The CachingOptimizer will be in state EMPTY_OPTIMIZER after the call.

source

Mock optimizer

Printing

MathOptInterface.Utilities.latex_formulationFunction
latex_formulation(model::MOI.ModelLike; kwargs...)

Wrap model in a type so that it can be pretty-printed as text/latex in a notebook like IJulia, or in Documenter.

To render the model, end the cell with latex_formulation(model), or call display(latex_formulation(model)) in to force the display of the model from inside a function.

Possible keyword arguments are:

  • simplify_coefficients : Simplify coefficients if possible by omitting them or removing trailing zeros.
  • default_name : The name given to variables with an empty name.
  • print_types : Print the MOI type of each function and set for clarity.
source

Copy utilities

MathOptInterface.Utilities.ModelFilterType
ModelFilter(filter::Function, model::MOI.ModelLike)

A layer to filter out various components of model.

The filter function takes a single argument, which is each element from the list returned by the attributes below. It returns true if the element should be visible in the filtered model and false otherwise.

The components that are filtered are:

  • Entire constraint types via:
    • MOI.ListOfConstraintTypesPresent
  • Individual constraints via:
    • MOI.ListOfConstraintIndices{F,S}
  • Specific attributes via:
    • MOI.ListOfModelAttributesSet
    • MOI.ListOfConstraintAttributesSet
    • MOI.ListOfVariableAttributesSet
Warning

The list of attributes filtered may change in a future release. You should write functions that are generic and not limited to the five types listed above. Thus, you should probably define a fallback filter(::Any) = true.

See below for examples of how this works.

Note

This layer has a limited scope. It is intended by be used in conjunction with MOI.copy_to.

Example: copy model excluding integer constraints

Use the do syntax to provide a single function.

filtered_src = MOI.Utilities.ModelFilter(src) do item
+end

A struct storing F-in-S constraints as a mapping between the constraint indices to the corresponding tuple of function and set.

source
MathOptInterface.Utilities.@struct_of_constraints_by_function_typesMacro
Utilities.@struct_of_constraints_by_function_types(name, func_types...)

Given a vector of n function types (F1, F2,..., Fn) in func_types, defines a subtype of StructOfConstraints of name name and which type parameters {T, C1, C2, ..., Cn}. It contains n field where the ith field has type Ci and stores the constraints of function type Fi.

The expression Fi can also be a union in which case any constraint for which the function type is in the union is stored in the field with type Ci.

source
MathOptInterface.Utilities.@struct_of_constraints_by_set_typesMacro
Utilities.@struct_of_constraints_by_set_types(name, func_types...)

Given a vector of n set types (S1, S2,..., Sn) in func_types, defines a subtype of StructOfConstraints of name name and which type parameters {T, C1, C2, ..., Cn}. It contains n field where the ith field has type Ci and stores the constraints of set type Si. The expression Si can also be a union in which case any constraint for which the set type is in the union is stored in the field with type Ci. This can be useful if Ci is a MatrixOfConstraints in order to concatenate the coefficients of constraints of several different set types in the same matrix.

source
MathOptInterface.Utilities.struct_of_constraint_codeFunction
struct_of_constraint_code(struct_name, types, field_types = nothing)

Given a vector of n Union{SymbolFun,_UnionSymbolFS{SymbolFun}} or Union{SymbolSet,_UnionSymbolFS{SymbolSet}} in types, defines a subtype of StructOfConstraints of name name and which type parameters {T, F1, F2, ..., Fn} if field_types is nothing and a {T} otherwise. It contains n field where the ith field has type Ci if field_types is nothing and type field_types[i] otherwise. If types is vector of Union{SymbolFun,_UnionSymbolFS{SymbolFun}} (resp. Union{SymbolSet,_UnionSymbolFS{SymbolSet}}) then the constraints of that function (resp. set) type are stored in the corresponding field.

This function is used by the macros @model, @struct_of_constraints_by_function_types and @struct_of_constraints_by_set_types.

source

Caching optimizer

MathOptInterface.Utilities.CachingOptimizerType
CachingOptimizer

CachingOptimizer is an intermediate layer that stores a cache of the model and links it with an optimizer. It supports incremental model construction and modification even when the optimizer doesn't.

Constructors

    CachingOptimizer(cache::MOI.ModelLike, optimizer::AbstractOptimizer)

Creates a CachingOptimizer in AUTOMATIC mode, with the optimizer optimizer.

The type of the optimizer returned is CachingOptimizer{typeof(optimizer), typeof(cache)} so it does not support the function reset_optimizer(::CachingOptimizer, new_optimizer) if the type of new_optimizer is different from the type of optimizer.

    CachingOptimizer(cache::MOI.ModelLike, mode::CachingOptimizerMode)

Creates a CachingOptimizer in the NO_OPTIMIZER state and mode mode.

The type of the optimizer returned is CachingOptimizer{MOI.AbstractOptimizer,typeof(cache)} so it does support the function reset_optimizer(::CachingOptimizer, new_optimizer) if the type of new_optimizer is different from the type of optimizer.

About the type

States

A CachingOptimizer may be in one of three possible states (CachingOptimizerState):

  • NO_OPTIMIZER: The CachingOptimizer does not have any optimizer.
  • EMPTY_OPTIMIZER: The CachingOptimizer has an empty optimizer. The optimizer is not synchronized with the cached model.
  • ATTACHED_OPTIMIZER: The CachingOptimizer has an optimizer, and it is synchronized with the cached model.

Modes

A CachingOptimizer has two modes of operation (CachingOptimizerMode):

  • MANUAL: The only methods that change the state of the CachingOptimizer are Utilities.reset_optimizer, Utilities.drop_optimizer, and Utilities.attach_optimizer. Attempting to perform an operation in the incorrect state results in an error.
  • AUTOMATIC: The CachingOptimizer changes its state when necessary. For example, optimize! will automatically call attach_optimizer (an optimizer must have been previously set). Attempting to add a constraint or perform a modification not supported by the optimizer results in a drop to EMPTY_OPTIMIZER mode.
source
MathOptInterface.Utilities.attach_optimizerFunction
attach_optimizer(model::CachingOptimizer)

Attaches the optimizer to model, copying all model data into it. Can be called only from the EMPTY_OPTIMIZER state. If the copy succeeds, the CachingOptimizer will be in state ATTACHED_OPTIMIZER after the call, otherwise an error is thrown; see MOI.copy_to for more details on which errors can be thrown.

source
MathOptInterface.Utilities.reset_optimizerFunction
reset_optimizer(m::CachingOptimizer, optimizer::MOI.AbstractOptimizer)

Sets or resets m to have the given empty optimizer optimizer.

Can be called from any state. An assertion error will be thrown if optimizer is not empty.

The CachingOptimizer m will be in state EMPTY_OPTIMIZER after the call.

source
reset_optimizer(m::CachingOptimizer)

Detaches and empties the current optimizer. Can be called from ATTACHED_OPTIMIZER or EMPTY_OPTIMIZER state. The CachingOptimizer will be in state EMPTY_OPTIMIZER after the call.

source

Mock optimizer

Printing

MathOptInterface.Utilities.latex_formulationFunction
latex_formulation(model::MOI.ModelLike; kwargs...)

Wrap model in a type so that it can be pretty-printed as text/latex in a notebook like IJulia, or in Documenter.

To render the model, end the cell with latex_formulation(model), or call display(latex_formulation(model)) in to force the display of the model from inside a function.

Possible keyword arguments are:

  • simplify_coefficients : Simplify coefficients if possible by omitting them or removing trailing zeros.
  • default_name : The name given to variables with an empty name.
  • print_types : Print the MOI type of each function and set for clarity.
source

Copy utilities

MathOptInterface.Utilities.ModelFilterType
ModelFilter(filter::Function, model::MOI.ModelLike)

A layer to filter out various components of model.

The filter function takes a single argument, which is each element from the list returned by the attributes below. It returns true if the element should be visible in the filtered model and false otherwise.

The components that are filtered are:

  • Entire constraint types via:
    • MOI.ListOfConstraintTypesPresent
  • Individual constraints via:
    • MOI.ListOfConstraintIndices{F,S}
  • Specific attributes via:
    • MOI.ListOfModelAttributesSet
    • MOI.ListOfConstraintAttributesSet
    • MOI.ListOfVariableAttributesSet
Warning

The list of attributes filtered may change in a future release. You should write functions that are generic and not limited to the five types listed above. Thus, you should probably define a fallback filter(::Any) = true.

See below for examples of how this works.

Note

This layer has a limited scope. It is intended by be used in conjunction with MOI.copy_to.

Example: copy model excluding integer constraints

Use the do syntax to provide a single function.

filtered_src = MOI.Utilities.ModelFilter(src) do item
     return item != (MOI.VariableIndex, MOI.Integer)
 end
 MOI.copy_to(dest, filtered_src)

Example: copy model excluding names

Use type dispatch to simplify the implementation:

my_filter(::Any) = true  # Note the generic fallback
@@ -100,7 +100,7 @@
     return status != MOI.NOT_IN_CONFLICT
 end
 filtered_src = MOI.Utilities.ModelFilter(my_filter, src)
-MOI.copy_to(dest, filtered_src)
source
MathOptInterface.Utilities.loadfromstring!Function
loadfromstring!(model, s)

A utility function to aid writing tests.

Warning

This function is not intended for widespread use. It is mainly used as a tool to simplify writing tests in MathOptInterface. Do not use it as an exchange format for storing or transmitting problem instances. Use the FileFormats submodule instead.

Example

julia> model = MOI.Utilities.Model{Float64}();
+MOI.copy_to(dest, filtered_src)
source
MathOptInterface.Utilities.loadfromstring!Function
loadfromstring!(model, s)

A utility function to aid writing tests.

Warning

This function is not intended for widespread use. It is mainly used as a tool to simplify writing tests in MathOptInterface. Do not use it as an exchange format for storing or transmitting problem instances. Use the FileFormats submodule instead.

Example

julia> model = MOI.Utilities.Model{Float64}();
 
 julia> MOI.Utilities.loadfromstring!(model, """
        variables: x, y, z
@@ -109,7 +109,7 @@
        con1: x + y <= 1.0
        con2: [x, y] in Nonnegatives(2)
        x >= 0.0
-       """)

Notes

Special labels are:

  • variables
  • minobjective
  • maxobjectives

Everything else denotes a constraint with a name.

Append ::T to use an element type of T when parsing the function.

Do not name VariableIndex constraints.

Exceptions

  • x - y does NOT currently parse. Instead, write x + -1.0 * y.
  • x^2 does NOT currently parse. Instead, write x * x.
source

Penalty relaxation

MathOptInterface.Utilities.PenaltyRelaxationType
PenaltyRelaxation(
+       """)

Notes

Special labels are:

  • variables
  • minobjective
  • maxobjectives

Everything else denotes a constraint with a name.

Append ::T to use an element type of T when parsing the function.

Do not name VariableIndex constraints.

Exceptions

  • x - y does NOT currently parse. Instead, write x + -1.0 * y.
  • x^2 does NOT currently parse. Instead, write x * x.
source

Penalty relaxation

MathOptInterface.Utilities.PenaltyRelaxationType
PenaltyRelaxation(
     penalties = Dict{MOI.ConstraintIndex,Float64}();
     default::Union{Nothing,T} = 1.0,
 )

A problem modifier that, when passed to MOI.modify, destructively modifies the model in-place to create a penalized relaxation of the constraints.

Warning

This is a destructive routine that modifies the model in-place. If you don't want to modify the original model, use JuMP.copy_model to create a copy before calling MOI.modify.

Reformulation

See Utilities.ScalarPenaltyRelaxation for details of the reformulation.

For each constraint ci, the penalty passed to Utilities.ScalarPenaltyRelaxation is get(penalties, ci, default). If the value is nothing, because ci does not exist in penalties and default = nothing, then the constraint is skipped.

Return value

MOI.modify(model, PenaltyRelaxation()) returns a Dict{MOI.ConstraintIndex,MOI.ScalarAffineFunction} that maps each constraint index to the corresponding y + z as a MOI.ScalarAffineFunction. In an optimal solution, query the value of these functions to compute the violation of each constraint.

Relax a subset of constraints

To relax a subset of constraints, pass a penalties dictionary and set default = nothing.

Supported constraint types

The penalty relaxation is currently limited to modifying MOI.ScalarAffineFunction and MOI.ScalarQuadraticFunction constraints in the linear sets MOI.LessThan, MOI.GreaterThan, MOI.EqualTo and MOI.Interval.

It does not include variable bound or integrality constraints, because these cannot be modified in-place.

To modify variable bounds, rewrite them as linear constraints.

Example

julia> model = MOI.Utilities.Model{Float64}();
@@ -154,7 +154,7 @@
  v[2] >= 0.0
 
 julia> map[c] isa MOI.ScalarAffineFunction{Float64}
-true
source
MathOptInterface.Utilities.ScalarPenaltyRelaxationType
ScalarPenaltyRelaxation(penalty::T) where {T}

A problem modifier that, when passed to MOI.modify, destructively modifies the constraint in-place to create a penalized relaxation of the constraint.

Warning

This is a destructive routine that modifies the constraint in-place. If you don't want to modify the original model, use JuMP.copy_model to create a copy before calling MOI.modify.

Reformulation

The penalty relaxation modifies constraints of the form $f(x) \in S$ into $f(x) + y - z \in S$, where $y, z \ge 0$, and then it introduces a penalty term into the objective of $a \times (y + z)$ (if minimizing, else $-a$), where $a$ is penalty

When S is MOI.LessThan or MOI.GreaterThan, we omit y or z respectively as a performance optimization.

Return value

MOI.modify(model, ci, ScalarPenaltyRelaxation(penalty)) returns y + z as a MOI.ScalarAffineFunction. In an optimal solution, query the value of this function to compute the violation of the constraint.

Example

julia> model = MOI.Utilities.Model{Float64}();
+true
source
MathOptInterface.Utilities.ScalarPenaltyRelaxationType
ScalarPenaltyRelaxation(penalty::T) where {T}

A problem modifier that, when passed to MOI.modify, destructively modifies the constraint in-place to create a penalized relaxation of the constraint.

Warning

This is a destructive routine that modifies the constraint in-place. If you don't want to modify the original model, use JuMP.copy_model to create a copy before calling MOI.modify.

Reformulation

The penalty relaxation modifies constraints of the form $f(x) \in S$ into $f(x) + y - z \in S$, where $y, z \ge 0$, and then it introduces a penalty term into the objective of $a \times (y + z)$ (if minimizing, else $-a$), where $a$ is penalty

When S is MOI.LessThan or MOI.GreaterThan, we omit y or z respectively as a performance optimization.

Return value

MOI.modify(model, ci, ScalarPenaltyRelaxation(penalty)) returns y + z as a MOI.ScalarAffineFunction. In an optimal solution, query the value of this function to compute the violation of the constraint.

Example

julia> model = MOI.Utilities.Model{Float64}();
 
 julia> x = MOI.add_variable(model);
 
@@ -175,18 +175,18 @@
  v[2] >= 0.0
 
 julia> f isa MOI.ScalarAffineFunction{Float64}
-true
source

MatrixOfConstraints

MatrixOfConstraints

MathOptInterface.Utilities.MatrixOfConstraintsType
mutable struct MatrixOfConstraints{T,AT,BT,ST} <: MOI.ModelLike
     coefficients::AT
     constants::BT
     sets::ST
     caches::Vector{Any}
     are_indices_mapped::Vector{BitSet}
     final_touch::Bool
-end

Represent ScalarAffineFunction and VectorAffinefunction constraints in a matrix form where the linear coefficients of the functions are stored in the coefficients field, the constants of the functions or sets are stored in the constants field. Additional information about the sets are stored in the sets field.

This model can only be used as the constraints field of a MOI.Utilities.AbstractModel.

When the constraints are added, they are stored in the caches field. They are only loaded in the coefficients and constants fields once MOI.Utilities.final_touch is called. For this reason, MatrixOfConstraints should not be used by an incremental interface. Use MOI.copy_to instead.

The constraints can be added in two different ways:

  1. With add_constraint, in which case a canonicalized copy of the function is stored in caches.
  2. With pass_nonvariable_constraints, in which case the functions and sets are stored themselves in caches without mapping the variable indices. The corresponding index in caches is added in are_indices_mapped. This avoids doing a copy of the function in case the getter of CanonicalConstraintFunction does not make a copy for the source model, for example, this is the case of VectorOfConstraints.

We illustrate this with an example. Suppose a model is copied from a src::MOI.Utilities.Model to a bridged model with a MatrixOfConstraints. For all the types that are not bridged, the constraints will be copied with pass_nonvariable_constraints. Hence the functions stored in caches are exactly the same as the ones stored in src. This is ok since this is only during the copy_to operation during which src cannot be modified. On the other hand, for the types that are bridged, the functions added may contain duplicates even if the functions did not contain duplicates in src so duplicates are removed with MOI.Utilities.canonical.

Interface

The .coefficients::AT type must implement:

The .constants::BT type must implement:

The .sets::ST type must implement:

source

.coefficients

MathOptInterface.Utilities.load_termsFunction
load_terms(coefficients, index_map, func, offset)::Nothing

Loads the terms of func to coefficients, mapping the variable indices with index_map.

The ith dimension of func is loaded at the (offset + i)th row of coefficients.

The function must be allocated first with allocate_terms.

The function func must be canonicalized, see is_canonical.

source
MathOptInterface.Utilities.final_touchFunction
final_touch(coefficients)::Nothing

Informs the coefficients that all functions have been added with load_terms. No more modification is allowed unless MOI.empty! is called.

final_touch(sets)::Nothing

Informs the sets that all functions have been added with add_set. No more modification is allowed unless MOI.empty! is called.

source
MathOptInterface.Utilities.extract_functionFunction
extract_function(coefficients, row::Integer, constant::T) where {T}

Return the MOI.ScalarAffineFunction{T} function corresponding to row row in coefficients.

extract_function(
+end

Represent ScalarAffineFunction and VectorAffinefunction constraints in a matrix form where the linear coefficients of the functions are stored in the coefficients field, the constants of the functions or sets are stored in the constants field. Additional information about the sets are stored in the sets field.

This model can only be used as the constraints field of a MOI.Utilities.AbstractModel.

When the constraints are added, they are stored in the caches field. They are only loaded in the coefficients and constants fields once MOI.Utilities.final_touch is called. For this reason, MatrixOfConstraints should not be used by an incremental interface. Use MOI.copy_to instead.

The constraints can be added in two different ways:

  1. With add_constraint, in which case a canonicalized copy of the function is stored in caches.
  2. With pass_nonvariable_constraints, in which case the functions and sets are stored themselves in caches without mapping the variable indices. The corresponding index in caches is added in are_indices_mapped. This avoids doing a copy of the function in case the getter of CanonicalConstraintFunction does not make a copy for the source model, for example, this is the case of VectorOfConstraints.

We illustrate this with an example. Suppose a model is copied from a src::MOI.Utilities.Model to a bridged model with a MatrixOfConstraints. For all the types that are not bridged, the constraints will be copied with pass_nonvariable_constraints. Hence the functions stored in caches are exactly the same as the ones stored in src. This is ok since this is only during the copy_to operation during which src cannot be modified. On the other hand, for the types that are bridged, the functions added may contain duplicates even if the functions did not contain duplicates in src so duplicates are removed with MOI.Utilities.canonical.

Interface

The .coefficients::AT type must implement:

The .constants::BT type must implement:

The .sets::ST type must implement:

source

.coefficients

MathOptInterface.Utilities.load_termsFunction
load_terms(coefficients, index_map, func, offset)::Nothing

Loads the terms of func to coefficients, mapping the variable indices with index_map.

The ith dimension of func is loaded at the (offset + i)th row of coefficients.

The function must be allocated first with allocate_terms.

The function func must be canonicalized, see is_canonical.

source
MathOptInterface.Utilities.final_touchFunction
final_touch(coefficients)::Nothing

Informs the coefficients that all functions have been added with load_terms. No more modification is allowed unless MOI.empty! is called.

final_touch(sets)::Nothing

Informs the sets that all functions have been added with add_set. No more modification is allowed unless MOI.empty! is called.

source
MathOptInterface.Utilities.extract_functionFunction
extract_function(coefficients, row::Integer, constant::T) where {T}

Return the MOI.ScalarAffineFunction{T} function corresponding to row row in coefficients.

extract_function(
     coefficients,
     rows::UnitRange,
     constants::Vector{T},
-) where{T}

Return the MOI.VectorAffineFunction{T} function corresponding to rows rows in coefficients.

source
MathOptInterface.Utilities.MutableSparseMatrixCSCType
mutable struct MutableSparseMatrixCSC{Tv,Ti<:Integer,I<:AbstractIndexing}
     indexing::I
     m::Int
     n::Int
@@ -194,15 +194,15 @@
     rowval::Vector{Ti}
     nzval::Vector{Tv}
     nz_added::Vector{Ti}
-end

Matrix type loading sparse matrices in the Compressed Sparse Column format. The indexing used is indexing, see AbstractIndexing. The other fields have the same meaning than for SparseArrays.SparseMatrixCSC except that the indexing is different unless indexing is OneBasedIndexing. In addition, nz_added is used to cache the number of non-zero terms that have been added to each column due to the incremental nature of load_terms.

The matrix is loaded in 5 steps:

  1. MOI.empty! is called.
  2. MOI.Utilities.add_column and MOI.Utilities.allocate_terms are called in any order.
  3. MOI.Utilities.set_number_of_rows is called.
  4. MOI.Utilities.load_terms is called for each affine function.
  5. MOI.Utilities.final_touch is called.
source
MathOptInterface.Utilities.ZeroBasedIndexingType
struct ZeroBasedIndexing <: AbstractIndexing end

Zero-based indexing: the ith row or column has index i - 1. This is useful when the vectors of row and column indices need to be communicated to a library using zero-based indexing such as C libraries.

source

.constants

MathOptInterface.Utilities.load_constantsFunction
load_constants(constants, offset, func_or_set)::Nothing

This function loads the constants of func_or_set in constants at an offset of offset. Where offset is the sum of the dimensions of the constraints already loaded. The storage should be preallocated with resize! before calling this function.

This function should be implemented to be usable as storage of constants for MatrixOfConstraints.

The constants are loaded in three steps:

  1. Base.empty! is called.
  2. Base.resize! is called with the sum of the dimensions of all constraints.
  3. MOI.Utilities.load_constants is called for each function for vector constraint or set for scalar constraint.
source
MathOptInterface.Utilities.modify_constantsFunction
modify_constants(constants, row::Integer, new_constant::T) where {T}
+end

Matrix type loading sparse matrices in the Compressed Sparse Column format. The indexing used is indexing, see AbstractIndexing. The other fields have the same meaning than for SparseArrays.SparseMatrixCSC except that the indexing is different unless indexing is OneBasedIndexing. In addition, nz_added is used to cache the number of non-zero terms that have been added to each column due to the incremental nature of load_terms.

The matrix is loaded in 5 steps:

  1. MOI.empty! is called.
  2. MOI.Utilities.add_column and MOI.Utilities.allocate_terms are called in any order.
  3. MOI.Utilities.set_number_of_rows is called.
  4. MOI.Utilities.load_terms is called for each affine function.
  5. MOI.Utilities.final_touch is called.
source
MathOptInterface.Utilities.ZeroBasedIndexingType
struct ZeroBasedIndexing <: AbstractIndexing end

Zero-based indexing: the ith row or column has index i - 1. This is useful when the vectors of row and column indices need to be communicated to a library using zero-based indexing such as C libraries.

source

.constants

MathOptInterface.Utilities.load_constantsFunction
load_constants(constants, offset, func_or_set)::Nothing

This function loads the constants of func_or_set in constants at an offset of offset. Where offset is the sum of the dimensions of the constraints already loaded. The storage should be preallocated with resize! before calling this function.

This function should be implemented to be usable as storage of constants for MatrixOfConstraints.

The constants are loaded in three steps:

  1. Base.empty! is called.
  2. Base.resize! is called with the sum of the dimensions of all constraints.
  3. MOI.Utilities.load_constants is called for each function for vector constraint or set for scalar constraint.
source

.sets

MathOptInterface.Utilities.set_indexFunction
set_index(sets, ::Type{S})::Union{Int,Nothing} where {S<:MOI.AbstractSet}

Return an integer corresponding to the index of the set type in the list given by set_types.

If S is not part of the list, return nothing.

source
MathOptInterface.Utilities.add_setFunction
add_set(sets, i)::Int64

Add a scalar set of type index i.

add_set(sets, i, dim)::Int64

Add a vector set of type index i and dimension dim.

Both methods return a unique Int64 of the set that can be used to reference this set.

source
MathOptInterface.Utilities.rowsFunction
rows(sets, ci::MOI.ConstraintIndex)::Union{Int,UnitRange{Int}}

Return the rows in 1:MOI.dimension(sets) corresponding to the set of id ci.value.

For scalar sets, this returns an Int. For vector sets, this returns an UnitRange{Int}.

source
MathOptInterface.Utilities.num_rowsFunction
num_rows(sets::OrderedProductOfSets, ::Type{S}) where {S}

Return the number of rows corresponding to a set of type S. That is, it is the sum of the dimensions of the sets of type S.

source

.sets

MathOptInterface.Utilities.set_indexFunction
set_index(sets, ::Type{S})::Union{Int,Nothing} where {S<:MOI.AbstractSet}

Return an integer corresponding to the index of the set type in the list given by set_types.

If S is not part of the list, return nothing.

source
MathOptInterface.Utilities.add_setFunction
add_set(sets, i)::Int64

Add a scalar set of type index i.

add_set(sets, i, dim)::Int64

Add a vector set of type index i and dimension dim.

Both methods return a unique Int64 of the set that can be used to reference this set.

source
MathOptInterface.Utilities.rowsFunction
rows(sets, ci::MOI.ConstraintIndex)::Union{Int,UnitRange{Int}}

Return the rows in 1:MOI.dimension(sets) corresponding to the set of id ci.value.

For scalar sets, this returns an Int. For vector sets, this returns an UnitRange{Int}.

source
MathOptInterface.Utilities.num_rowsFunction
num_rows(sets::OrderedProductOfSets, ::Type{S}) where {S}

Return the number of rows corresponding to a set of type S. That is, it is the sum of the dimensions of the sets of type S.

source

Fallbacks

Fallbacks

MathOptInterface.Utilities.get_fallbackFunction
get_fallback(model::MOI.ModelLike, ::MOI.ObjectiveValue)

Compute the objective function value using the VariablePrimal results and the ObjectiveFunction value.

source
get_fallback(
     model::MOI.ModelLike,
     ::MOI.DualObjectiveValue,
     ::Type{T},
-)::T where {T}

Compute the dual objective value of type T using the ConstraintDual results and the ConstraintFunction and ConstraintSet values.

Note that the nonlinear part of the model is ignored.

source
get_fallback(
+)::T where {T}

Compute the dual objective value of type T using the ConstraintDual results and the ConstraintFunction and ConstraintSet values.

Note that the nonlinear part of the model is ignored.

source
get_fallback(
     model::MOI.ModelLike,
     ::MOI.ConstraintPrimal,
     constraint_index::MOI.ConstraintIndex,
-)

Compute the value of the function of the constraint of index constraint_index using the VariablePrimal results and the ConstraintFunction values.

source
get_fallback(
+)

Compute the value of the function of the constraint of index constraint_index using the VariablePrimal results and the ConstraintFunction values.

source
get_fallback(
     model::MOI.ModelLike,
     attr::MOI.ConstraintDual,
     ci::MOI.ConstraintIndex{Union{MOI.VariableIndex,MOI.VectorOfVariables}},
     ::Type{T} = Float64,
-) where {T}

Compute the dual of the constraint of index ci using the ConstraintDual of other constraints and the ConstraintFunction values.

Throws an error if some constraints are quadratic or if there is one another MOI.VariableIndex-in-S or MOI.VectorOfVariables-in-S constraint with one of the variables in the function of the constraint ci.

source

Function utilities

The following utilities are available for functions:

MathOptInterface.Utilities.eval_variablesFunction
eval_variables(value_fn::Function, f::MOI.AbstractFunction)

Returns the value of function f if each variable index vi is evaluated as value_fn(vi).

Note that value_fn must return a Number. See substitute_variables for a similar function where value_fn returns an MOI.AbstractScalarFunction.

Warning

The two-argument version of eval_variables is deprecated and may be removed in MOI v2.0.0. Use the three-argument method eval_variables(::Function, ::MOI.ModelLike, ::MOI.AbstractFunction) instead.

source
MathOptInterface.Utilities.map_indicesFunction
map_indices(index_map::Function, attr::MOI.AnyAttribute, x::X)::X where {X}

Substitute any MOI.VariableIndex (resp. MOI.ConstraintIndex) in x by the MOI.VariableIndex (resp. MOI.ConstraintIndex) of the same type given by index_map(x).

When to implement this method for new types X

This function is used by implementations of MOI.copy_to on constraint functions, attribute values and submittable values. If you define a new attribute whose values x::X contain variable or constraint indices, you must also implement this function.

source
map_indices(
+) where {T}

Compute the dual of the constraint of index ci using the ConstraintDual of other constraints and the ConstraintFunction values.

Throws an error if some constraints are quadratic or if there is one another MOI.VariableIndex-in-S or MOI.VectorOfVariables-in-S constraint with one of the variables in the function of the constraint ci.

source

Function utilities

The following utilities are available for functions:

MathOptInterface.Utilities.eval_variablesFunction
eval_variables(value_fn::Function, f::MOI.AbstractFunction)

Returns the value of function f if each variable index vi is evaluated as value_fn(vi).

Note that value_fn must return a Number. See substitute_variables for a similar function where value_fn returns an MOI.AbstractScalarFunction.

Warning

The two-argument version of eval_variables is deprecated and may be removed in MOI v2.0.0. Use the three-argument method eval_variables(::Function, ::MOI.ModelLike, ::MOI.AbstractFunction) instead.

source
MathOptInterface.Utilities.map_indicesFunction
map_indices(index_map::Function, attr::MOI.AnyAttribute, x::X)::X where {X}

Substitute any MOI.VariableIndex (resp. MOI.ConstraintIndex) in x by the MOI.VariableIndex (resp. MOI.ConstraintIndex) of the same type given by index_map(x).

When to implement this method for new types X

This function is used by implementations of MOI.copy_to on constraint functions, attribute values and submittable values. If you define a new attribute whose values x::X contain variable or constraint indices, you must also implement this function.

source
map_indices(
     variable_map::AbstractDict{T,T},
     x::X,
-)::X where {T<:MOI.Index,X}

Shortcut for map_indices(vi -> variable_map[vi], x).

source
MathOptInterface.Utilities.substitute_variablesFunction
substitute_variables(variable_map::Function, x)

Substitute any MOI.VariableIndex in x by variable_map(x). The variable_map function returns either MOI.VariableIndex or MOI.ScalarAffineFunction, see eval_variables for a similar function where variable_map returns a number.

This function is used by bridge optimizers on constraint functions, attribute values and submittable values when at least one variable bridge is used hence it needs to be implemented for custom types that are meant to be used as attribute or submittable value.

Note

When implementing a new method, don't use substitute_variables(::Function, because Julia will not specialize on it. Use instead substitute_variables(::F, ...) where {F<:Function}.

source
MathOptInterface.Utilities.filter_variablesFunction
filter_variables(keep::Function, f::AbstractFunction)

Return a new function f with the variable vi such that !keep(vi) removed.

WARNING: Don't define filter_variables(::Function, ...) because Julia will not specialize on this. Define instead filter_variables(::F, ...) where {F<:Function}.

source
MathOptInterface.Utilities.remove_variableFunction
remove_variable(f::AbstractFunction, vi::VariableIndex)

Return a new function f with the variable vi removed.

source
remove_variable(
+)::X where {T<:MOI.Index,X}

Shortcut for map_indices(vi -> variable_map[vi], x).

source
MathOptInterface.Utilities.substitute_variablesFunction
substitute_variables(variable_map::Function, x)

Substitute any MOI.VariableIndex in x by variable_map(x). The variable_map function returns either MOI.VariableIndex or MOI.ScalarAffineFunction, see eval_variables for a similar function where variable_map returns a number.

This function is used by bridge optimizers on constraint functions, attribute values and submittable values when at least one variable bridge is used hence it needs to be implemented for custom types that are meant to be used as attribute or submittable value.

Note

When implementing a new method, don't use substitute_variables(::Function, because Julia will not specialize on it. Use instead substitute_variables(::F, ...) where {F<:Function}.

source
MathOptInterface.Utilities.filter_variablesFunction
filter_variables(keep::Function, f::AbstractFunction)

Return a new function f with the variable vi such that !keep(vi) removed.

WARNING: Don't define filter_variables(::Function, ...) because Julia will not specialize on this. Define instead filter_variables(::F, ...) where {F<:Function}.

source
MathOptInterface.Utilities.remove_variableFunction
remove_variable(f::AbstractFunction, vi::VariableIndex)

Return a new function f with the variable vi removed.

source
remove_variable(
     f::MOI.AbstractFunction,
     s::MOI.AbstractSet,
     vi::MOI.VariableIndex,
-)

Return a tuple (g, t) representing the constraint f-in-s with the variable vi removed. That is, the terms containing the variable vi in the function f are removed and the dimension of the set s is updated if needed (for example, when f is a VectorOfVariables with vi being one of the variables).

source
MathOptInterface.Utilities.all_coefficientsFunction
all_coefficients(p::Function, f::MOI.AbstractFunction)

Determine whether predicate p returns true for all coefficients of f, returning false as soon as the first coefficient of f for which p returns false is encountered (short-circuiting). Similar to all.

source
MathOptInterface.Utilities.unsafe_addFunction
unsafe_add(t1::MOI.ScalarAffineTerm, t2::MOI.ScalarAffineTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.ScalarAffineTerm. It is unsafe because it uses the variable of t1 as the variable of the output without checking that it is equal to that of t2.

source
unsafe_add(t1::MOI.ScalarQuadraticTerm, t2::MOI.ScalarQuadraticTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.ScalarQuadraticTerm. It is unsafe because it uses the variable's of t1 as the variable's of the output without checking that they are the same (up to permutation) to those of t2.

source
unsafe_add(t1::MOI.VectorAffineTerm, t2::MOI.VectorAffineTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.VectorAffineTerm. It is unsafe because it uses the output_index and variable of t1 as the output_index and variable of the output term without checking that they are equal to those of t2.

source
MathOptInterface.Utilities.isapprox_zeroFunction
isapprox_zero(f::MOI.AbstractFunction, tol)

Return a Bool indicating whether the function f is approximately zero using tol as a tolerance.

Important note

This function assumes that f does not contain any duplicate terms, you might want to first call canonical if that is not guaranteed.

Example

julia> import MathOptInterface as MOI
+)

Return a tuple (g, t) representing the constraint f-in-s with the variable vi removed. That is, the terms containing the variable vi in the function f are removed and the dimension of the set s is updated if needed (for example, when f is a VectorOfVariables with vi being one of the variables).

source
MathOptInterface.Utilities.all_coefficientsFunction
all_coefficients(p::Function, f::MOI.AbstractFunction)

Determine whether predicate p returns true for all coefficients of f, returning false as soon as the first coefficient of f for which p returns false is encountered (short-circuiting). Similar to all.

source
MathOptInterface.Utilities.unsafe_addFunction
unsafe_add(t1::MOI.ScalarAffineTerm, t2::MOI.ScalarAffineTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.ScalarAffineTerm. It is unsafe because it uses the variable of t1 as the variable of the output without checking that it is equal to that of t2.

source
unsafe_add(t1::MOI.ScalarQuadraticTerm, t2::MOI.ScalarQuadraticTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.ScalarQuadraticTerm. It is unsafe because it uses the variable's of t1 as the variable's of the output without checking that they are the same (up to permutation) to those of t2.

source
unsafe_add(t1::MOI.VectorAffineTerm, t2::MOI.VectorAffineTerm)

Sums the coefficients of t1 and t2 and returns an output MOI.VectorAffineTerm. It is unsafe because it uses the output_index and variable of t1 as the output_index and variable of the output term without checking that they are equal to those of t2.

source
MathOptInterface.Utilities.isapprox_zeroFunction
isapprox_zero(f::MOI.AbstractFunction, tol)

Return a Bool indicating whether the function f is approximately zero using tol as a tolerance.

Important note

This function assumes that f does not contain any duplicate terms, you might want to first call canonical if that is not guaranteed.

Example

julia> import MathOptInterface as MOI
 
 julia> x = MOI.VariableIndex(1)
 MOI.VariableIndex(1)
@@ -250,7 +250,7 @@
 false
 
 julia> MOI.Utilities.isapprox_zero(MOI.Utilities.canonical(f), 1e-8)
-true
source
MathOptInterface.Utilities.zero_with_output_dimensionFunction
zero_with_output_dimension(::Type{T}, output_dimension::Integer) where {T}

Create an instance of type T with the output dimension output_dimension.

This is mostly useful in Bridges, when code needs to be agnostic to the type of vector-valued function that is passed in.

source

The following functions can be used to canonicalize a function:

MathOptInterface.Utilities.is_canonicalFunction
is_canonical(f::Union{ScalarAffineFunction, VectorAffineFunction})

Returns a Bool indicating whether the function is in canonical form. See canonical.

source
is_canonical(f::Union{ScalarQuadraticFunction, VectorQuadraticFunction})

Returns a Bool indicating whether the function is in canonical form. See canonical.

source
MathOptInterface.Utilities.canonicalFunction
canonical(f::MOI.AbstractFunction)

Returns the function in a canonical form, that is,

  • A term appear only once.
  • The coefficients are nonzero.
  • The terms appear in increasing order of variable where there the order of the variables is the order of their value.
  • For a AbstractVectorFunction, the terms are sorted in ascending order of output index.

The output of canonical can be assumed to be a copy of f, even for VectorOfVariables.

Example

julia> import MathOptInterface as MOI
+true
source
MathOptInterface.Utilities.zero_with_output_dimensionFunction
zero_with_output_dimension(::Type{T}, output_dimension::Integer) where {T}

Create an instance of type T with the output dimension output_dimension.

This is mostly useful in Bridges, when code needs to be agnostic to the type of vector-valued function that is passed in.

source

The following functions can be used to canonicalize a function:

MathOptInterface.Utilities.is_canonicalFunction
is_canonical(f::Union{ScalarAffineFunction, VectorAffineFunction})

Returns a Bool indicating whether the function is in canonical form. See canonical.

source
is_canonical(f::Union{ScalarQuadraticFunction, VectorQuadraticFunction})

Returns a Bool indicating whether the function is in canonical form. See canonical.

source
MathOptInterface.Utilities.canonicalFunction
canonical(f::MOI.AbstractFunction)

Returns the function in a canonical form, that is,

  • A term appear only once.
  • The coefficients are nonzero.
  • The terms appear in increasing order of variable where there the order of the variables is the order of their value.
  • For a AbstractVectorFunction, the terms are sorted in ascending order of output index.

The output of canonical can be assumed to be a copy of f, even for VectorOfVariables.

Example

julia> import MathOptInterface as MOI
 
 julia> x, y, z = MOI.VariableIndex.(1:3);
 
@@ -261,25 +261,25 @@
 5.0 + 2.0 MOI.VariableIndex(2) + 1.0 MOI.VariableIndex(1) + 3.0 MOI.VariableIndex(3) - 2.0 MOI.VariableIndex(1) - 3.0 MOI.VariableIndex(3)
 
 julia> MOI.Utilities.canonical(f)
-5.0 - 1.0 MOI.VariableIndex(1) + 2.0 MOI.VariableIndex(2)
source
MathOptInterface.Utilities.canonicalize!Function
canonicalize!(f::Union{ScalarAffineFunction, VectorAffineFunction})

Convert a function to canonical form in-place, without allocating a copy to hold the result. See canonical.

source
canonicalize!(f::Union{ScalarQuadraticFunction, VectorQuadraticFunction})

Convert a function to canonical form in-place, without allocating a copy to hold the result. See canonical.

source

The following functions can be used to manipulate functions with basic algebra:

MathOptInterface.Utilities.scalarizeFunction
scalarize(func::MOI.VectorOfVariables, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.SingleVariable}.

See also eachscalar.

source
scalarize(func::MOI.VectorAffineFunction{T}, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.ScalarAffineFunction{T}}.

See also eachscalar.

source
scalarize(func::MOI.VectorQuadraticFunction{T}, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.ScalarQuadraticFunction{T}}.

See also eachscalar.

source
MathOptInterface.Utilities.canonicalize!Function
canonicalize!(f::Union{ScalarAffineFunction, VectorAffineFunction})

Convert a function to canonical form in-place, without allocating a copy to hold the result. See canonical.

source
canonicalize!(f::Union{ScalarQuadraticFunction, VectorQuadraticFunction})

Convert a function to canonical form in-place, without allocating a copy to hold the result. See canonical.

source

The following functions can be used to manipulate functions with basic algebra:

MathOptInterface.Utilities.scalarizeFunction
scalarize(func::MOI.VectorOfVariables, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.SingleVariable}.

See also eachscalar.

source
scalarize(func::MOI.VectorAffineFunction{T}, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.ScalarAffineFunction{T}}.

See also eachscalar.

source
scalarize(func::MOI.VectorQuadraticFunction{T}, ignore_constants::Bool = false)

Returns a vector of scalar functions making up the vector function in the form of a Vector{MOI.ScalarQuadraticFunction{T}}.

See also eachscalar.

source
MathOptInterface.Utilities.promote_operationFunction
promote_operation(
     op::Function,
     ::Type{T},
     ArgsTypes::Type{<:Union{T,AbstractVector{T},MOI.AbstractFunction}}...,
-) where {T<:Number}

Compute the return type of the call operate(op, T, args...), where the types of the arguments args are ArgsTypes.

One assumption is that the element type T is invariant under each operation. That is, op(::T, ::T)::T where op is a +, -, *, and /.

There are six methods for which we implement Utilities.promote_operation:

  1. + a. promote_operation(::typeof(+), ::Type{T}, ::Type{F1}, ::Type{F2})
  2. - a. promote_operation(::typeof(-), ::Type{T}, ::Type{F}) b. promote_operation(::typeof(-), ::Type{T}, ::Type{F1}, ::Type{F2})
  3. * a. promote_operation(::typeof(*), ::Type{T}, ::Type{T}, ::Type{F}) b. promote_operation(::typeof(*), ::Type{T}, ::Type{F}, ::Type{T}) c. promote_operation(::typeof(*), ::Type{T}, ::Type{F1}, ::Type{F2}) where F1 and F2 are VariableIndex or ScalarAffineFunction d. promote_operation(::typeof(*), ::Type{T}, ::Type{<:Diagonal{T}}, ::Type{F}
  4. / a. promote_operation(::typeof(/), ::Type{T}, ::Type{F}, ::Type{T})
  5. vcat a. promote_operation(::typeof(vcat), ::Type{T}, ::Type{F}...)
  6. imag a. promote_operation(::typeof(imag), ::Type{T}, ::Type{F}) where F is VariableIndex or VectorOfVariables

In each case, F (or F1 and F2) is one of the ten supported types, with a restriction that the mathematical operation makes sense, for example, we don't define promote_operation(-, T, F1, F2) where F1 is a scalar-valued function and F2 is a vector-valued function. The ten supported types are:

  1. ::T
  2. ::VariableIndex
  3. ::ScalarAffineFunction{T}
  4. ::ScalarQuadraticFunction{T}
  5. ::ScalarNonlinearFunction
  6. ::AbstractVector{T}
  7. ::VectorOfVariables
  8. ::VectorAffineFunction{T}
  9. ::VectorQuadraticFunction{T}
  10. ::VectorNonlinearFunction
source
MathOptInterface.Utilities.operateFunction
operate(
+) where {T<:Number}

Compute the return type of the call operate(op, T, args...), where the types of the arguments args are ArgsTypes.

One assumption is that the element type T is invariant under each operation. That is, op(::T, ::T)::T where op is a +, -, *, and /.

There are six methods for which we implement Utilities.promote_operation:

  1. + a. promote_operation(::typeof(+), ::Type{T}, ::Type{F1}, ::Type{F2})
  2. - a. promote_operation(::typeof(-), ::Type{T}, ::Type{F}) b. promote_operation(::typeof(-), ::Type{T}, ::Type{F1}, ::Type{F2})
  3. * a. promote_operation(::typeof(*), ::Type{T}, ::Type{T}, ::Type{F}) b. promote_operation(::typeof(*), ::Type{T}, ::Type{F}, ::Type{T}) c. promote_operation(::typeof(*), ::Type{T}, ::Type{F1}, ::Type{F2}) where F1 and F2 are VariableIndex or ScalarAffineFunction d. promote_operation(::typeof(*), ::Type{T}, ::Type{<:Diagonal{T}}, ::Type{F}
  4. / a. promote_operation(::typeof(/), ::Type{T}, ::Type{F}, ::Type{T})
  5. vcat a. promote_operation(::typeof(vcat), ::Type{T}, ::Type{F}...)
  6. imag a. promote_operation(::typeof(imag), ::Type{T}, ::Type{F}) where F is VariableIndex or VectorOfVariables

In each case, F (or F1 and F2) is one of the ten supported types, with a restriction that the mathematical operation makes sense, for example, we don't define promote_operation(-, T, F1, F2) where F1 is a scalar-valued function and F2 is a vector-valued function. The ten supported types are:

  1. ::T
  2. ::VariableIndex
  3. ::ScalarAffineFunction{T}
  4. ::ScalarQuadraticFunction{T}
  5. ::ScalarNonlinearFunction
  6. ::AbstractVector{T}
  7. ::VectorOfVariables
  8. ::VectorAffineFunction{T}
  9. ::VectorQuadraticFunction{T}
  10. ::VectorNonlinearFunction
source
MathOptInterface.Utilities.operateFunction
operate(
     op::Function,
     ::Type{T},
     args::Union{T,MOI.AbstractFunction}...,
-)::MOI.AbstractFunction where {T<:Number}

Returns an MOI.AbstractFunction representing the function resulting from the operation op(args...) on functions of coefficient type T.

No argument can be modified.

Methods

  1. + a. operate(::typeof(+), ::Type{T}, ::F1) b. operate(::typeof(+), ::Type{T}, ::F1, ::F2) c. operate(::typeof(+), ::Type{T}, ::F1...)
  2. - a. operate(::typeof(-), ::Type{T}, ::F) b. operate(::typeof(-), ::Type{T}, ::F1, ::F2)
  3. * a. operate(::typeof(*), ::Type{T}, ::T, ::F) b. operate(::typeof(*), ::Type{T}, ::F, ::T) c. operate(::typeof(*), ::Type{T}, ::F1, ::F2) where F1 and F2 are VariableIndex or ScalarAffineFunction d. operate(::typeof(*), ::Type{T}, ::Diagonal{T}, ::F)
  4. / a. operate(::typeof(/), ::Type{T}, ::F, ::T)
  5. vcat a. operate(::typeof(vcat), ::Type{T}, ::F...)
  6. imag a. operate(::typeof(imag), ::Type{T}, ::F) where F is VariableIndex or VectorOfVariables

One assumption is that the element type T is invariant under each operation. That is, op(::T, ::T)::T where op is a +, -, *, and /.

In each case, F (or F1 and F2) is one of the ten supported types, with a restriction that the mathematical operation makes sense, for example, we don't define promote_operation(-, T, F1, F2) where F1 is a scalar-valued function and F2 is a vector-valued function. The ten supported types are:

  1. ::T
  2. ::VariableIndex
  3. ::ScalarAffineFunction{T}
  4. ::ScalarQuadraticFunction{T}
  5. ::ScalarNonlinearFunction
  6. ::AbstractVector{T}
  7. ::VectorOfVariables
  8. ::VectorAffineFunction{T}
  9. ::VectorQuadraticFunction{T}
  10. ::VectorNonlinearFunction
source
MathOptInterface.Utilities.operate!Function
operate!(
+)::MOI.AbstractFunction where {T<:Number}

Returns an MOI.AbstractFunction representing the function resulting from the operation op(args...) on functions of coefficient type T.

No argument can be modified.

Methods

  1. + a. operate(::typeof(+), ::Type{T}, ::F1) b. operate(::typeof(+), ::Type{T}, ::F1, ::F2) c. operate(::typeof(+), ::Type{T}, ::F1...)
  2. - a. operate(::typeof(-), ::Type{T}, ::F) b. operate(::typeof(-), ::Type{T}, ::F1, ::F2)
  3. * a. operate(::typeof(*), ::Type{T}, ::T, ::F) b. operate(::typeof(*), ::Type{T}, ::F, ::T) c. operate(::typeof(*), ::Type{T}, ::F1, ::F2) where F1 and F2 are VariableIndex or ScalarAffineFunction d. operate(::typeof(*), ::Type{T}, ::Diagonal{T}, ::F)
  4. / a. operate(::typeof(/), ::Type{T}, ::F, ::T)
  5. vcat a. operate(::typeof(vcat), ::Type{T}, ::F...)
  6. imag a. operate(::typeof(imag), ::Type{T}, ::F) where F is VariableIndex or VectorOfVariables

One assumption is that the element type T is invariant under each operation. That is, op(::T, ::T)::T where op is a +, -, *, and /.

In each case, F (or F1 and F2) is one of the ten supported types, with a restriction that the mathematical operation makes sense, for example, we don't define promote_operation(-, T, F1, F2) where F1 is a scalar-valued function and F2 is a vector-valued function. The ten supported types are:

  1. ::T
  2. ::VariableIndex
  3. ::ScalarAffineFunction{T}
  4. ::ScalarQuadraticFunction{T}
  5. ::ScalarNonlinearFunction
  6. ::AbstractVector{T}
  7. ::VectorOfVariables
  8. ::VectorAffineFunction{T}
  9. ::VectorQuadraticFunction{T}
  10. ::VectorNonlinearFunction
source
MathOptInterface.Utilities.operate!Function
operate!(
     op::Function,
     ::Type{T},
     args::Union{T,MOI.AbstractFunction}...,
-)::MOI.AbstractFunction where {T<:Number}

Returns an MOI.AbstractFunction representing the function resulting from the operation op(args...) on functions of coefficient type T.

The first argument may be modified, in which case the return value is identical to the first argument. For operations which cannot be implemented in-place, this function returns a new object.

source
MathOptInterface.Utilities.operate_output_index!Function
operate_output_index!(
+)::MOI.AbstractFunction where {T<:Number}

Returns an MOI.AbstractFunction representing the function resulting from the operation op(args...) on functions of coefficient type T.

The first argument may be modified, in which case the return value is identical to the first argument. For operations which cannot be implemented in-place, this function returns a new object.

source
MathOptInterface.Utilities.operate_output_index!Function
operate_output_index!(
     op::Union{typeof(+),typeof(-)},
     ::Type{T},
     output_index::Integer,
     f::Union{AbstractVector{T},MOI.AbstractVectorFunction}
     g::Union{T,MOI.AbstractScalarFunction}...
-) where {T<:Number}

Return an MOI.AbstractVectorFunction in which the scalar function in row output_index is the result of op(f[output_index], g).

The functions at output index different to output_index are the same as the functions at the same output index in func. The first argument may be modified.

Methods

  1. + a. operate_output_index!(+, ::Type{T}, ::Int, ::VectorF, ::ScalarF)
  2. - a. operate_output_index!(-, ::Type{T}, ::Int, ::VectorF, ::ScalarF)
source
MathOptInterface.Utilities.vectorizeFunction
vectorize(x::AbstractVector{<:Number})

Returns x.

source
vectorize(x::AbstractVector{MOI.VariableIndex})

Returns the vector of scalar affine functions in the form of a MOI.VectorAffineFunction{T}.

source
vectorize(funcs::AbstractVector{MOI.ScalarAffineFunction{T}}) where T

Returns the vector of scalar affine functions in the form of a MOI.VectorAffineFunction{T}.

source
vectorize(funcs::AbstractVector{MOI.ScalarQuadraticFunction{T}}) where T

Returns the vector of scalar quadratic functions in the form of a MOI.VectorQuadraticFunction{T}.

source

Constraint utilities

The following utilities are available for moving the function constant to the set for scalar constraints:

MathOptInterface.Utilities.shift_constantFunction
shift_constant(set::MOI.AbstractScalarSet, offset)

Returns a new scalar set new_set such that func-in-set is equivalent to func + offset-in-new_set.

Use supports_shift_constant to check if the set supports shifting:

if MOI.Utilities.supports_shift_constant(typeof(set))
+) where {T<:Number}

Return an MOI.AbstractVectorFunction in which the scalar function in row output_index is the result of op(f[output_index], g).

The functions at output index different to output_index are the same as the functions at the same output index in func. The first argument may be modified.

Methods

  1. + a. operate_output_index!(+, ::Type{T}, ::Int, ::VectorF, ::ScalarF)
  2. - a. operate_output_index!(-, ::Type{T}, ::Int, ::VectorF, ::ScalarF)
source
MathOptInterface.Utilities.vectorizeFunction
vectorize(x::AbstractVector{<:Number})

Returns x.

source
vectorize(x::AbstractVector{MOI.VariableIndex})

Returns the vector of scalar affine functions in the form of a MOI.VectorAffineFunction{T}.

source
vectorize(funcs::AbstractVector{MOI.ScalarAffineFunction{T}}) where T

Returns the vector of scalar affine functions in the form of a MOI.VectorAffineFunction{T}.

source
vectorize(funcs::AbstractVector{MOI.ScalarQuadraticFunction{T}}) where T

Returns the vector of scalar quadratic functions in the form of a MOI.VectorQuadraticFunction{T}.

source

Constraint utilities

The following utilities are available for moving the function constant to the set for scalar constraints:

MathOptInterface.Utilities.shift_constantFunction
shift_constant(set::MOI.AbstractScalarSet, offset)

Returns a new scalar set new_set such that func-in-set is equivalent to func + offset-in-new_set.

Use supports_shift_constant to check if the set supports shifting:

if MOI.Utilities.supports_shift_constant(typeof(set))
     new_set = MOI.Utilities.shift_constant(set, -func.constant)
     func.constant = 0
     MOI.add_constraint(model, func, new_set)
@@ -294,30 +294,30 @@
 true
 
 julia> MOI.Utilities.shift_constant(set, 1.0)
-MathOptInterface.Interval{Float64}(-1.0, 4.0)
source
MathOptInterface.Utilities.normalize_and_add_constraintFunction
normalize_and_add_constraint(
     model::MOI.ModelLike,
     func::MOI.AbstractScalarFunction,
     set::MOI.AbstractScalarSet;
     allow_modify_function::Bool = false,
-)

Adds the scalar constraint obtained by moving the constant term in func to the set in model. If allow_modify_function is true then the function func can be modified.

source

The following utility identifies those constraints imposing bounds on a given variable, and returns those bound values:

MathOptInterface.Utilities.get_boundsFunction
get_bounds(model::MOI.ModelLike, ::Type{T}, x::MOI.VariableIndex)

Return a tuple (lb, ub) of type Tuple{T, T}, where lb and ub are lower and upper bounds, respectively, imposed on x in model.

source
get_bounds(
+)

Return the func-in-set constraint in normalized form. That is, if func is MOI.ScalarQuadraticFunction or MOI.ScalarAffineFunction, the constant is moved to the set. If allow_modify_function is true then the function func can be modified.

source

The following utility identifies those constraints imposing bounds on a given variable, and returns those bound values:

MathOptInterface.Utilities.get_boundsFunction
get_bounds(model::MOI.ModelLike, ::Type{T}, x::MOI.VariableIndex)

Return a tuple (lb, ub) of type Tuple{T, T}, where lb and ub are lower and upper bounds, respectively, imposed on x in model.

source
get_bounds(
     model::MOI.ModelLike,
     bounds_cache::Dict{MOI.VariableIndex,NTuple{2,T}},
     f::MOI.ScalarAffineFunction{T},
-) where {T} --> Union{Nothing,NTuple{2,T}}

Return the lower and upper bound of f as a tuple. If the domain is not bounded, return nothing.

source
get_bounds(
+) where {T} --> Union{Nothing,NTuple{2,T}}

Return the lower and upper bound of f as a tuple. If the domain is not bounded, return nothing.

source
get_bounds(
     model::MOI.ModelLike,
     bounds_cache::Dict{MOI.VariableIndex,NTuple{2,T}},
     x::MOI.VariableIndex,
-) where {T} --> Union{Nothing,NTuple{2,T}}

Return the lower and upper bound of x as a tuple. If the domain is not bounded, return nothing.

Similar to get_bounds(::MOI.ModelLike, ::Type{T}, ::MOI.VariableIndex), except that the second argument is a cache which maps variables to their bounds and avoids repeated lookups.

source

The following utilities are useful when working with symmetric matrix cones.

Set utilities

The following utilities are available for sets:

MathOptInterface.Utilities.ProjectionUpperBoundDistanceType
ProjectionUpperBoundDistance() <: AbstractDistance

An upper bound on the minimum distance between point and the closest feasible point in set.

Definition of distance

The minimum distance is computed as:

\[d(x, \mathcal{K}) = \min_{y \in \mathcal{K}} || x - y ||\]

where $x$ is point and $\mathcal{K}$ is set. The norm is computed as:

\[||x|| = \sqrt{f(x, x, \mathcal{K})}\]

where $f$ is Utilities.set_dot.

In the default case, where the set does not have a specialized method for Utilities.set_dot, the norm is equivalent to the Euclidean norm $||x|| = \sqrt{\sum x_i^2}$.

Why an upper bound?

In most cases, distance_to_set should return the smallest upper bound, but it may return a larger value if the smallest upper bound is expensive to compute.

For example, given an epigraph from of a conic set, $\{(t, x) | f(x) \le t\}$, it may be simpler to return $\delta$ such that $f(x) \le t + \delta$, rather than computing the nearest projection onto the set.

If the distance is not the smallest upper bound, the docstring of the appropriate distance_to_set method must describe the way that the distance is computed.

source
MathOptInterface.Utilities.distance_to_setFunction
distance_to_set(
+) where {T} --> Union{Nothing,NTuple{2,T}}

Return the lower and upper bound of x as a tuple. If the domain is not bounded, return nothing.

Similar to get_bounds(::MOI.ModelLike, ::Type{T}, ::MOI.VariableIndex), except that the second argument is a cache which maps variables to their bounds and avoids repeated lookups.

source

The following utilities are useful when working with symmetric matrix cones.

Set utilities

The following utilities are available for sets:

MathOptInterface.Utilities.ProjectionUpperBoundDistanceType
ProjectionUpperBoundDistance() <: AbstractDistance

An upper bound on the minimum distance between point and the closest feasible point in set.

Definition of distance

The minimum distance is computed as:

\[d(x, \mathcal{K}) = \min_{y \in \mathcal{K}} || x - y ||\]

where $x$ is point and $\mathcal{K}$ is set. The norm is computed as:

\[||x|| = \sqrt{f(x, x, \mathcal{K})}\]

where $f$ is Utilities.set_dot.

In the default case, where the set does not have a specialized method for Utilities.set_dot, the norm is equivalent to the Euclidean norm $||x|| = \sqrt{\sum x_i^2}$.

Why an upper bound?

In most cases, distance_to_set should return the smallest upper bound, but it may return a larger value if the smallest upper bound is expensive to compute.

For example, given an epigraph from of a conic set, $\{(t, x) | f(x) \le t\}$, it may be simpler to return $\delta$ such that $f(x) \le t + \delta$, rather than computing the nearest projection onto the set.

If the distance is not the smallest upper bound, the docstring of the appropriate distance_to_set method must describe the way that the distance is computed.

source
MathOptInterface.Utilities.distance_to_setFunction
distance_to_set(
     [d::AbstractDistance = ProjectionUpperBoundDistance()],]
     point::T,
     set::MOI.AbstractScalarSet,
@@ -327,11 +327,11 @@
     [d::AbstractDistance = ProjectionUpperBoundDistance(),]
     point::AbstractVector{T},
     set::MOI.AbstractVectorSet,
-) where {T}

Compute the distance between point and set using the distance metric d. If point is in the set set, this function must return zero(T).

If d is omitted, the default distance is Utilities.ProjectionUpperBoundDistance.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.RotatedSecondOrderCone)

Let (t, u, y...) = x. Return the 2-norm of the vector d such that in x + d, u is projected to 1 if u <= 0, and t is increased such that x + d belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.ExponentialCone)

Let (u, v, w) = x. If v > 0, return the epigraph distance d such that (u, v, w + d) belongs to the set.

If v <= 0 return the 2-norm of the vector d such that x + d = (u, 1, z) where z satisfies the constraints.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.DualExponentialCone)

Let (u, v, w) = x. If u < 0, return the epigraph distance d such that (u, v, w + d) belongs to the set.

If u >= 0 return the 2-norm of the vector d such that x + d = (u, -1, z) where z satisfies the constraints.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.GeometricMeanCone)

Let (t, y...) = x. If all y are non-negative, return the epigraph distance d such that (t + d, y...) belongs to the set.

If any y are strictly negative, return the 2-norm of the vector d that projects negative y elements to 0 and t to ℝ₋.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.PowerCone)

Let (a, b, c) = x. If a and b are non-negative, return the epigraph distance required to increase c such that the constraint is satisfied.

If a or b is strictly negative, return the 2-norm of the vector d such that in the vector x + d: c, and any negative a and b are projected to 0.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.DualPowerCone)

Let (a, b, c) = x. If a and b are non-negative, return the epigraph distance required to increase c such that the constraint is satisfied.

If a or b is strictly negative, return the 2-norm of the vector d such that in the vector x + d: c, and any negative a and b are projected to 0.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.NormOneCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.NormInfinityCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.RelativeEntropyCone)

Let (u, v..., w...) = x. If v and w are strictly positive, return the epigraph distance required to increase u such that the constraint is satisfied.

If any elements in v or w are non-positive, return the 2-norm of the vector d such that in the vector x + d: any non-positive elements in v and w are projected to 1, and u is projected such that the epigraph constraint holds.

source
distance_to_set(::ProjectionUpperBoundDistance, x, set::MOI.NormCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
MathOptInterface.Utilities.set_dotFunction
set_dot(x::AbstractVector, y::AbstractVector, set::AbstractVectorSet)

Return the scalar product between a vector x of the set set and a vector y of the dual of the set s.

source
set_dot(x, y, set::AbstractScalarSet)

Return the scalar product between a number x of the set set and a number y of the dual of the set s.

source

DoubleDicts

MathOptInterface.Utilities.DoubleDicts.DoubleDictType
DoubleDict{V}

An optimized dictionary to map MOI.ConstraintIndex to values of type V.

Works as a AbstractDict{MOI.ConstraintIndex,V} with minimal differences.

If V is also a MOI.ConstraintIndex, use IndexDoubleDict.

Note that MOI.ConstraintIndex is not a concrete type, opposed to MOI.ConstraintIndex{MOI.VariableIndex, MOI.Integers}, which is a concrete type.

When looping through multiple keys of the same Function-in-Set type, use

inner = dict[F, S]

to return a type-stable DoubleDictInner.

source
MathOptInterface.Utilities.DoubleDicts.outer_keysFunction
outer_keys(d::AbstractDoubleDict)

Return an iterator over the outer keys of the AbstractDoubleDict d. Each outer key is a Tuple{Type,Type} so that a double loop can be easily used:

for (F, S) in DoubleDicts.outer_keys(dict)
+) where {T}

Compute the distance between point and set using the distance metric d. If point is in the set set, this function must return zero(T).

If d is omitted, the default distance is Utilities.ProjectionUpperBoundDistance.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.RotatedSecondOrderCone)

Let (t, u, y...) = x. Return the 2-norm of the vector d such that in x + d, u is projected to 1 if u <= 0, and t is increased such that x + d belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.ExponentialCone)

Let (u, v, w) = x. If v > 0, return the epigraph distance d such that (u, v, w + d) belongs to the set.

If v <= 0 return the 2-norm of the vector d such that x + d = (u, 1, z) where z satisfies the constraints.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.DualExponentialCone)

Let (u, v, w) = x. If u < 0, return the epigraph distance d such that (u, v, w + d) belongs to the set.

If u >= 0 return the 2-norm of the vector d such that x + d = (u, -1, z) where z satisfies the constraints.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.GeometricMeanCone)

Let (t, y...) = x. If all y are non-negative, return the epigraph distance d such that (t + d, y...) belongs to the set.

If any y are strictly negative, return the 2-norm of the vector d that projects negative y elements to 0 and t to ℝ₋.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.PowerCone)

Let (a, b, c) = x. If a and b are non-negative, return the epigraph distance required to increase c such that the constraint is satisfied.

If a or b is strictly negative, return the 2-norm of the vector d such that in the vector x + d: c, and any negative a and b are projected to 0.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.DualPowerCone)

Let (a, b, c) = x. If a and b are non-negative, return the epigraph distance required to increase c such that the constraint is satisfied.

If a or b is strictly negative, return the 2-norm of the vector d such that in the vector x + d: c, and any negative a and b are projected to 0.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.NormOneCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.NormInfinityCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
distance_to_set(::ProjectionUpperBoundDistance, x, ::MOI.RelativeEntropyCone)

Let (u, v..., w...) = x. If v and w are strictly positive, return the epigraph distance required to increase u such that the constraint is satisfied.

If any elements in v or w are non-positive, return the 2-norm of the vector d such that in the vector x + d: any non-positive elements in v and w are projected to 1, and u is projected such that the epigraph constraint holds.

source
distance_to_set(::ProjectionUpperBoundDistance, x, set::MOI.NormCone)

Let (t, y...) = x. Return the epigraph distance d such that (t + d, y...) belongs to the set.

source
MathOptInterface.Utilities.set_dotFunction
set_dot(x::AbstractVector, y::AbstractVector, set::AbstractVectorSet)

Return the scalar product between a vector x of the set set and a vector y of the dual of the set s.

source
set_dot(x, y, set::AbstractScalarSet)

Return the scalar product between a number x of the set set and a number y of the dual of the set s.

source

DoubleDicts

MathOptInterface.Utilities.DoubleDicts.DoubleDictType
DoubleDict{V}

An optimized dictionary to map MOI.ConstraintIndex to values of type V.

Works as a AbstractDict{MOI.ConstraintIndex,V} with minimal differences.

If V is also a MOI.ConstraintIndex, use IndexDoubleDict.

Note that MOI.ConstraintIndex is not a concrete type, opposed to MOI.ConstraintIndex{MOI.VariableIndex, MOI.Integers}, which is a concrete type.

When looping through multiple keys of the same Function-in-Set type, use

inner = dict[F, S]

to return a type-stable DoubleDictInner.

source
MathOptInterface.Utilities.DoubleDicts.outer_keysFunction
outer_keys(d::AbstractDoubleDict)

Return an iterator over the outer keys of the AbstractDoubleDict d. Each outer key is a Tuple{Type,Type} so that a double loop can be easily used:

for (F, S) in DoubleDicts.outer_keys(dict)
     for (k, v) in dict[F, S]
         # ...
     end
-end

For performance, it is recommended that the inner loop lies in a separate function to guarantee type-stability. Some outer keys (F, S) might lead to an empty dict[F, S]. If you want only nonempty dict[F, S], use nonempty_outer_keys.

source
MathOptInterface.Utilities.DoubleDicts.nonempty_outer_keysFunction
nonempty_outer_keys(d::AbstractDoubleDict)

Return a vector of outer keys of the AbstractDoubleDict d.

Only outer keys that have a nonempty set of inner keys will be returned.

Each outer key is a Tuple{Type,Type} so that a double loop can be easily used

for (F, S) in DoubleDicts.nonempty_outer_keys(dict)
+end

For performance, it is recommended that the inner loop lies in a separate function to guarantee type-stability. Some outer keys (F, S) might lead to an empty dict[F, S]. If you want only nonempty dict[F, S], use nonempty_outer_keys.

source
MathOptInterface.Utilities.DoubleDicts.nonempty_outer_keysFunction
nonempty_outer_keys(d::AbstractDoubleDict)

Return a vector of outer keys of the AbstractDoubleDict d.

Only outer keys that have a nonempty set of inner keys will be returned.

Each outer key is a Tuple{Type,Type} so that a double loop can be easily used

for (F, S) in DoubleDicts.nonempty_outer_keys(dict)
     for (k, v) in dict[F, S]
         # ...
     end
@@ -339,4 +339,4 @@
 For performance, it is recommended that the inner loop lies in a separate
 function to guarantee type-stability.
 
-If you want an iterator of all current outer keys, use [`outer_keys`](@ref).
source
+If you want an iterator of all current outer keys, use [`outer_keys`](@ref).source diff --git a/dev/tutorials/bridging_constraint/index.html b/dev/tutorials/bridging_constraint/index.html index db9c7b8728..a9f16fc8df 100644 --- a/dev/tutorials/bridging_constraint/index.html +++ b/dev/tutorials/bridging_constraint/index.html @@ -99,4 +99,4 @@ end

Bridge deletion

When a bridge is deleted, the constraints it added must be deleted too.

function delete(model::ModelLike, bridge::SignBridge)
     delete(model, bridge.constraint)
     return
-end
+end diff --git a/dev/tutorials/example/index.html b/dev/tutorials/example/index.html index a8cf03e730..4de31c9f80 100644 --- a/dev/tutorials/example/index.html +++ b/dev/tutorials/example/index.html @@ -42,4 +42,4 @@ 3-element Vector{Float64}: 1.0 1.0 - 1.0 + 1.0 diff --git a/dev/tutorials/implementing/index.html b/dev/tutorials/implementing/index.html index d35cff4834..8169a74d81 100644 --- a/dev/tutorials/implementing/index.html +++ b/dev/tutorials/implementing/index.html @@ -111,4 +111,4 @@ n = # Code to get NumberOfObjectives return n end

Then, the user can write:

model = Gurobi.Optimizer()
-MOI.set(model, Gurobi.NumberofObjectives(), 3)
+MOI.set(model, Gurobi.NumberofObjectives(), 3) diff --git a/dev/tutorials/latency/index.html b/dev/tutorials/latency/index.html index 72683fa7e3..1507ad60a0 100644 --- a/dev/tutorials/latency/index.html +++ b/dev/tutorials/latency/index.html @@ -126,4 +126,4 @@ end

You can create a flame-graph via

using SnoopCompile
 tinf = @snoopi_deep example_diet(GLPK.Optimizer, true)
 using ProfileView
-ProfileView.view(flamegraph(tinf))

Here's how things looked in mid-August 2021: flamegraph

There are a few opportunities for improvement (non-red flames, particularly on the right). But the main problem is a large red (non-precompilable due to method ownership) flame.

+ProfileView.view(flamegraph(tinf))

Here's how things looked in mid-August 2021: flamegraph

There are a few opportunities for improvement (non-red flames, particularly on the right). But the main problem is a large red (non-precompilable due to method ownership) flame.

diff --git a/dev/tutorials/manipulating_expressions/index.html b/dev/tutorials/manipulating_expressions/index.html index 6e9bf25c71..6348009126 100644 --- a/dev/tutorials/manipulating_expressions/index.html +++ b/dev/tutorials/manipulating_expressions/index.html @@ -19,4 +19,4 @@ 2-element Vector{MathOptInterface.ScalarAffineFunction{Int64}}: (2) + (1) MOI.VariableIndex(1) (4) + (2) MOI.VariableIndex(1)
Note

Utilities.eachscalar returns an iterator on the dimensions, which serves the same purpose as Utilities.scalarize.

output_dimension returns the number of dimensions of the output of a function:

julia> MOI.output_dimension(g)
-2
+2 diff --git a/dev/tutorials/mathprogbase/index.html b/dev/tutorials/mathprogbase/index.html index afea03762a..b560322b71 100644 --- a/dev/tutorials/mathprogbase/index.html +++ b/dev/tutorials/mathprogbase/index.html @@ -51,4 +51,4 @@ objval = objective_value(model), sol = value.(x) ) -end +end