Skip to content

Commit f182c14

Browse files
committed
[Test] add tests from Gurobi.jl's nonlinear update
1 parent c4fbc48 commit f182c14

File tree

1 file changed

+347
-0
lines changed

1 file changed

+347
-0
lines changed

src/Test/test_nonlinear.jl

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,3 +1836,350 @@ function setup_test(
18361836
)
18371837
return
18381838
end
1839+
1840+
function test_nonlinear_with_scalar_quadratic_function_with_off_diag(
1841+
model::MOI.ModelLike,
1842+
config::Config{T},
1843+
) where {T}
1844+
@requires T == Float64
1845+
@requires _supports(config, MOI.optimize!)
1846+
F = MOI.ScalarNonlinearFunction
1847+
@requires MOI.supports_constraint(model, F, MOI.EqualTo{T})
1848+
for (a, b, status) in [
1849+
(1, 2, config.optimal_status),
1850+
(1, 3, config.infeasible_status),
1851+
(2, 3, config.optimal_status),
1852+
(2, 4, config.infeasible_status),
1853+
]
1854+
MOI.empty!(model)
1855+
x, _ = MOI.add_constrained_variable(model, MOI.EqualTo(T(2)))
1856+
y, _ = MOI.add_constrained_variable(model, MOI.EqualTo(T(3)))
1857+
g = T(a) * x * y
1858+
@test g isa MOI.ScalarQuadraticFunction{T}
1859+
f = MOI.ScalarNonlinearFunction(:sqrt, Any[g])
1860+
MOI.add_constraint(model, f, MOI.GreaterThan(T(b)))
1861+
MOI.optimize!(model)
1862+
@test MOI.get(model, MOI.TerminationStatus()) == status
1863+
end
1864+
return
1865+
end
1866+
1867+
function setup_test(
1868+
::typeof(test_nonlinear_with_scalar_quadratic_function_with_off_diag),
1869+
model::MOIU.MockOptimizer,
1870+
config::Config{T},
1871+
) where {T}
1872+
if T != Float64
1873+
return # Skip for non-Float64 solvers
1874+
end
1875+
MOI.Utilities.set_mock_optimize!(
1876+
model,
1877+
mock ->
1878+
MOI.Utilities.mock_optimize!(mock, config.optimal_status, T[2, 3]),
1879+
mock -> MOI.Utilities.mock_optimize!(mock, config.infeasible_status),
1880+
mock ->
1881+
MOI.Utilities.mock_optimize!(mock, config.optimal_status, T[2, 3]),
1882+
mock -> MOI.Utilities.mock_optimize!(mock, config.infeasible_status),
1883+
)
1884+
return
1885+
end
1886+
1887+
function test_nonlinear_constraint_log(
1888+
model::MOI.ModelLike,
1889+
config::Config{T},
1890+
) where {T}
1891+
F, S = MOI.ScalarNonlinearFunction, MOI.GreaterThan{T}
1892+
@requires MOI.supports_constraint(model, F, S)
1893+
x = MOI.add_variable(model)
1894+
t = MOI.add_variable(model)
1895+
MOI.add_constraint(model, x, MOI.LessThan(T(2)))
1896+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
1897+
f = 1.0 * t
1898+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1899+
g = MOI.ScalarNonlinearFunction(
1900+
:-,
1901+
Any[MOI.ScalarNonlinearFunction(:log, Any[x]), t],
1902+
)
1903+
c = MOI.add_constraint(model, g, MOI.GreaterThan(T(0)))
1904+
MOI.optimize!(model)
1905+
x_val = MOI.get(model, MOI.VariablePrimal(), x)
1906+
t_val = MOI.get(model, MOI.VariablePrimal(), t)
1907+
@test (x_val, T(2), config)
1908+
@test (t_val, log(x_val), config)
1909+
@test (MOI.get(model, MOI.ObjectiveValue()), t_val, config)
1910+
@test (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
1911+
@test c in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
1912+
return
1913+
end
1914+
1915+
function setup_test(
1916+
::typeof(test_nonlinear_constraint_log),
1917+
model::MOIU.MockOptimizer,
1918+
config::Config{T},
1919+
) where {T}
1920+
if T != Float64
1921+
return # Skip for non-Float64 solvers
1922+
end
1923+
MOI.Utilities.set_mock_optimize!(
1924+
model,
1925+
mock -> MOI.Utilities.mock_optimize!(
1926+
mock,
1927+
config.optimal_status,
1928+
T[2, log(2)],
1929+
),
1930+
)
1931+
return
1932+
end
1933+
1934+
function test_nonlinear_constraint_uminus(
1935+
model::MOI.ModelLike,
1936+
config::Config{T},
1937+
) where {T}
1938+
F, S = MOI.ScalarNonlinearFunction, MOI.GreaterThan{Float64}
1939+
@requires MOI.supports_constraint(model, F, S)
1940+
x = MOI.add_variable(model)
1941+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
1942+
f = T(1) * x
1943+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1944+
g = MOI.ScalarNonlinearFunction(:-, Any[x])
1945+
MOI.add_constraint(model, g, MOI.GreaterThan(T(-2)))
1946+
MOI.optimize!(model)
1947+
@test (MOI.get(model, MOI.VariablePrimal(), x), T(2), config)
1948+
@test (MOI.get(model, MOI.ObjectiveValue()), T(2), config)
1949+
return
1950+
end
1951+
1952+
function setup_test(
1953+
::typeof(test_nonlinear_constraint_uminus),
1954+
model::MOIU.MockOptimizer,
1955+
config::Config{T},
1956+
) where {T}
1957+
if T != Float64
1958+
return # Skip for non-Float64 solvers
1959+
end
1960+
MOI.Utilities.set_mock_optimize!(
1961+
model,
1962+
mock -> MOI.Utilities.mock_optimize!(mock, config.optimal_status, T[2]),
1963+
)
1964+
return
1965+
end
1966+
1967+
function test_nonlinear_constraint_scalar_affine_function(
1968+
model::MOI.ModelLike,
1969+
config::Config{T},
1970+
) where {T}
1971+
x1, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
1972+
x2, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
1973+
x3, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
1974+
x4, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
1975+
f = T(1) * x1 + T(2) * x2 + T(3) * x3
1976+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
1977+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1978+
h = T(1) * x1 + T(2) * x2 + T(3) * x3 + T(4) * x4
1979+
g = MOI.ScalarNonlinearFunction(:+, Any[h])
1980+
MOI.add_constraint(model, g, MOI.LessThan(T(6)))
1981+
MOI.optimize!(model)
1982+
@test (MOI.get(model, MOI.ObjectiveValue()), T(6), config)
1983+
return
1984+
end
1985+
1986+
function setup_test(
1987+
::typeof(test_nonlinear_constraint_scalar_affine_function),
1988+
model::MOIU.MockOptimizer,
1989+
config::Config{T},
1990+
) where {T}
1991+
if T != Float64
1992+
return # Skip for non-Float64 solvers
1993+
end
1994+
MOI.Utilities.set_mock_optimize!(
1995+
model,
1996+
mock -> MOI.Utilities.mock_optimize!(
1997+
mock,
1998+
config.optimal_status,
1999+
T[1, 1, 1, 0],
2000+
),
2001+
)
2002+
return
2003+
end
2004+
2005+
function test_nonlinear_quadratic_1(
2006+
model::MOI.ModelLike,
2007+
config::Config{T},
2008+
) where {T}
2009+
# max x + y
2010+
# s.t sqrt(*(x, x) + *(y, y)) <= 1
2011+
# x, y >= 0
2012+
#
2013+
# -> x = y = 1/sqrt(2)
2014+
x = MOI.add_variable(model)
2015+
y = MOI.add_variable(model)
2016+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2017+
f = T(1) * x + T(1) * y
2018+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
2019+
f1 = MOI.ScalarNonlinearFunction(:*, Any[x, x])
2020+
f2 = MOI.ScalarNonlinearFunction(:*, Any[y, y])
2021+
f3 = MOI.ScalarNonlinearFunction(:+, Any[f1, f2])
2022+
g = MOI.ScalarNonlinearFunction(:sqrt, Any[f3])
2023+
c = MOI.add_constraint(model, g, MOI.LessThan(T(1)))
2024+
MOI.optimize!(model)
2025+
@test (MOI.get(model, MOI.ObjectiveValue()), 2 / sqrt(2), config)
2026+
@test (MOI.get(model, MOI.VariablePrimal(), x), 1 / sqrt(2), config)
2027+
@test (MOI.get(model, MOI.VariablePrimal(), y), 1 / sqrt(2), config)
2028+
return
2029+
end
2030+
2031+
function setup_test(
2032+
::typeof(test_nonlinear_quadratic_1),
2033+
model::MOIU.MockOptimizer,
2034+
config::Config{T},
2035+
) where {T}
2036+
if T != Float64
2037+
return # Skip for non-Float64 solvers
2038+
end
2039+
MOI.Utilities.set_mock_optimize!(
2040+
model,
2041+
mock -> MOI.Utilities.mock_optimize!(
2042+
mock,
2043+
config.optimal_status,
2044+
T[1 / sqrt(2), 1 / sqrt(2)],
2045+
),
2046+
)
2047+
return
2048+
end
2049+
2050+
function test_nonlinear_quadratic_2(
2051+
model::MOI.ModelLike,
2052+
config::Config{T},
2053+
) where {T}
2054+
# Present products as ScalarAffineTerms nested in ScalarNonlinearFunctions
2055+
# max x + y
2056+
# s.t sqrt(*(x, x) + *(y, y)) <= 1
2057+
# x, y >= 0
2058+
#
2059+
# -> x = y = 1/sqrt(2)
2060+
x = MOI.add_variable(model)
2061+
y = MOI.add_variable(model)
2062+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2063+
f = T(1) * x + T(1) * y
2064+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
2065+
f1 = MOI.ScalarNonlinearFunction(:*, Any[T(1)*x, x])
2066+
f2 = MOI.ScalarNonlinearFunction(:*, Any[T(1)*y, y])
2067+
f3 = MOI.ScalarNonlinearFunction(:+, Any[f1, f2])
2068+
g = MOI.ScalarNonlinearFunction(:sqrt, Any[f3])
2069+
c = MOI.add_constraint(model, g, MOI.LessThan(T(1)))
2070+
MOI.optimize!(model)
2071+
@test (MOI.get(model, MOI.ObjectiveValue()), 2 / sqrt(2), config)
2072+
@test (MOI.get(model, MOI.VariablePrimal(), x), 1 / sqrt(2), config)
2073+
@test (MOI.get(model, MOI.VariablePrimal(), y), 1 / sqrt(2), config)
2074+
return
2075+
end
2076+
2077+
function setup_test(
2078+
::typeof(test_nonlinear_quadratic_2),
2079+
model::MOIU.MockOptimizer,
2080+
config::Config{T},
2081+
) where {T}
2082+
if T != Float64
2083+
return # Skip for non-Float64 solvers
2084+
end
2085+
MOI.Utilities.set_mock_optimize!(
2086+
model,
2087+
mock -> MOI.Utilities.mock_optimize!(
2088+
mock,
2089+
config.optimal_status,
2090+
T[1 / sqrt(2), 1 / sqrt(2)],
2091+
),
2092+
)
2093+
return
2094+
end
2095+
2096+
function test_nonlinear_quadratic_3(
2097+
model::MOI.ModelLike,
2098+
config::Config{T},
2099+
) where {T}
2100+
# Present products as ScalarQuadraticFunctions (complete with 2x factor ...)
2101+
# max x + y
2102+
# s.t sqrt(*(x, x) + *(y, y)) <= 1
2103+
# x, y >= 0
2104+
#
2105+
# -> x = y = 1/sqrt(2)
2106+
x = MOI.add_variable(model)
2107+
y = MOI.add_variable(model)
2108+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2109+
f = T(1) * x + T(1) * y
2110+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
2111+
f1 = T(1) * x * x
2112+
f2 = T(1) * y * y
2113+
f3 = MOI.ScalarNonlinearFunction(:+, Any[f1, f2])
2114+
g = MOI.ScalarNonlinearFunction(:sqrt, Any[f3])
2115+
c = MOI.add_constraint(model, g, MOI.LessThan(T(1)))
2116+
MOI.optimize!(model)
2117+
@test (MOI.get(model, MOI.ObjectiveValue()), 2 / sqrt(2), config)
2118+
@test (MOI.get(model, MOI.VariablePrimal(), x), 1 / sqrt(2), config)
2119+
@test (MOI.get(model, MOI.VariablePrimal(), y), 1 / sqrt(2), config)
2120+
return
2121+
end
2122+
2123+
function setup_test(
2124+
::typeof(test_nonlinear_quadratic_3),
2125+
model::MOIU.MockOptimizer,
2126+
config::Config{T},
2127+
) where {T}
2128+
if T != Float64
2129+
return # Skip for non-Float64 solvers
2130+
end
2131+
MOI.Utilities.set_mock_optimize!(
2132+
model,
2133+
mock -> MOI.Utilities.mock_optimize!(
2134+
mock,
2135+
config.optimal_status,
2136+
T[1 / sqrt(2), 1 / sqrt(2)],
2137+
),
2138+
)
2139+
return
2140+
end
2141+
2142+
function test_nonlinear_quadratic_4(
2143+
model::MOI.ModelLike,
2144+
config::Config{T},
2145+
) where {T}
2146+
# max x + y
2147+
# s.t sqrt(^(x, 2) + ^(y, 2)) <= 1 # Use NL POW(2) operator
2148+
# x, y >= 0
2149+
#
2150+
# -> x = y = 1/sqrt(2)
2151+
x = MOI.add_variable(model)
2152+
y = MOI.add_variable(model)
2153+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2154+
f = T(1) * x + T(1) * y
2155+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
2156+
f1 = MOI.ScalarNonlinearFunction(:^, Any[x, 2])
2157+
f2 = MOI.ScalarNonlinearFunction(:^, Any[y, 2])
2158+
f3 = MOI.ScalarNonlinearFunction(:+, Any[f1, f2])
2159+
g = MOI.ScalarNonlinearFunction(:sqrt, Any[f3])
2160+
c = MOI.add_constraint(model, g, MOI.LessThan(T(1)))
2161+
MOI.optimize!(model)
2162+
@test (MOI.get(model, MOI.ObjectiveValue()), 2 / sqrt(2), config)
2163+
@test (MOI.get(model, MOI.VariablePrimal(), x), 1 / sqrt(2), config)
2164+
@test (MOI.get(model, MOI.VariablePrimal(), y), 1 / sqrt(2), config)
2165+
return
2166+
end
2167+
2168+
function setup_test(
2169+
::typeof(test_nonlinear_quadratic_4),
2170+
model::MOIU.MockOptimizer,
2171+
config::Config{T},
2172+
) where {T}
2173+
if T != Float64
2174+
return # Skip for non-Float64 solvers
2175+
end
2176+
MOI.Utilities.set_mock_optimize!(
2177+
model,
2178+
mock -> MOI.Utilities.mock_optimize!(
2179+
mock,
2180+
config.optimal_status,
2181+
T[1 / sqrt(2), 1 / sqrt(2)],
2182+
),
2183+
)
2184+
return
2185+
end

0 commit comments

Comments
 (0)