@@ -1836,3 +1836,350 @@ function setup_test(
18361836 )
18371837 return
18381838end
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