PySCIPOpt 5.6.0__tar.gz → 5.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PKG-INFO +1 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PySCIPOpt.egg-info/PKG-INFO +1 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PySCIPOpt.egg-info/SOURCES.txt +5 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/pyproject.toml +7 -7
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/setup.py +32 -24
- pyscipopt-5.7.0/src/pyscipopt/__init__.py +58 -0
- pyscipopt-5.7.0/src/pyscipopt/_version.py +1 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/event.pxi +1 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/expr.pxi +60 -23
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/lp.pxi +8 -4
- pyscipopt-5.7.0/src/pyscipopt/matrix.pxi +112 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/nodesel.pxi +6 -2
- pyscipopt-5.7.0/src/pyscipopt/recipes/__init__.py +0 -0
- pyscipopt-5.7.0/src/pyscipopt/recipes/getLocalConss.py +51 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/recipes/infeasibilities.py +1 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/recipes/nonlinear.py +1 -2
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/scip.c +74243 -75947
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/scip.pxd +34 -13
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/scip.pxi +616 -71
- pyscipopt-5.7.0/src/pyscipopt/scip.pyi +1339 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_benders.py +0 -5
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_cons.py +61 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_customizedbenders.py +0 -10
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_expr.py +11 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_gomory.py +1 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_logical.py +1 -6
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_matrix_variable.py +204 -8
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_model.py +39 -4
- pyscipopt-5.7.0/tests/test_node_methods.py +183 -0
- pyscipopt-5.7.0/tests/test_recipe_getLocalConss.py +38 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_relax.py +2 -1
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_tsp.py +1 -2
- pyscipopt-5.7.0/tests/test_vars.py +146 -0
- pyscipopt-5.6.0/src/pyscipopt/__init__.py +0 -56
- pyscipopt-5.6.0/src/pyscipopt/_version.py +0 -1
- pyscipopt-5.6.0/src/pyscipopt/matrix.pxi +0 -139
- pyscipopt-5.6.0/tests/test_vars.py +0 -67
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/LICENSE +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PySCIPOpt.egg-info/dependency_links.txt +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PySCIPOpt.egg-info/requires.txt +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/PySCIPOpt.egg-info/top_level.txt +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/README.md +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/setup.cfg +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/Multidict.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/benders.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/benderscut.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/branchrule.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/conshdlr.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/cutsel.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/heuristic.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/presol.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/pricer.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/propagator.pxi +0 -0
- /pyscipopt-5.6.0/src/pyscipopt/recipes/__init__.py → /pyscipopt-5.7.0/src/pyscipopt/py.typed +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/reader.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/recipes/piecewise.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/recipes/primal_dual_evolution.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/relax.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/scip.pyx +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/src/pyscipopt/sepa.pxi +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_alldiff.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_bipartite.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_branch_mostinfeas.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_branch_probing_lp.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_conshdlr.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_copy.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_cutsel.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_event.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_heur.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_knapsack.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_linexpr.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_lp.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_memory.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_nlrow.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_node.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_nodesel.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_nogil.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_nonlinear.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_numerics.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_pricer.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_quadcons.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_quickprod.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_quicksum.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_reader.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_recipe_infeasibilities.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_recipe_nonlinear.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_recipe_piecewise.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_recipe_primal_dual_evolution.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_reopt.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_row_dual.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_short.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_solution.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_strong_branching.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_sub_sol.py +0 -0
- {pyscipopt-5.6.0 → pyscipopt-5.7.0}/tests/test_tree.py +0 -0
|
@@ -25,14 +25,17 @@ src/pyscipopt/nodesel.pxi
|
|
|
25
25
|
src/pyscipopt/presol.pxi
|
|
26
26
|
src/pyscipopt/pricer.pxi
|
|
27
27
|
src/pyscipopt/propagator.pxi
|
|
28
|
+
src/pyscipopt/py.typed
|
|
28
29
|
src/pyscipopt/reader.pxi
|
|
29
30
|
src/pyscipopt/relax.pxi
|
|
30
31
|
src/pyscipopt/scip.c
|
|
31
32
|
src/pyscipopt/scip.pxd
|
|
32
33
|
src/pyscipopt/scip.pxi
|
|
34
|
+
src/pyscipopt/scip.pyi
|
|
33
35
|
src/pyscipopt/scip.pyx
|
|
34
36
|
src/pyscipopt/sepa.pxi
|
|
35
37
|
src/pyscipopt/recipes/__init__.py
|
|
38
|
+
src/pyscipopt/recipes/getLocalConss.py
|
|
36
39
|
src/pyscipopt/recipes/infeasibilities.py
|
|
37
40
|
src/pyscipopt/recipes/nonlinear.py
|
|
38
41
|
src/pyscipopt/recipes/piecewise.py
|
|
@@ -60,6 +63,7 @@ tests/test_memory.py
|
|
|
60
63
|
tests/test_model.py
|
|
61
64
|
tests/test_nlrow.py
|
|
62
65
|
tests/test_node.py
|
|
66
|
+
tests/test_node_methods.py
|
|
63
67
|
tests/test_nodesel.py
|
|
64
68
|
tests/test_nogil.py
|
|
65
69
|
tests/test_nonlinear.py
|
|
@@ -69,6 +73,7 @@ tests/test_quadcons.py
|
|
|
69
73
|
tests/test_quickprod.py
|
|
70
74
|
tests/test_quicksum.py
|
|
71
75
|
tests/test_reader.py
|
|
76
|
+
tests/test_recipe_getLocalConss.py
|
|
72
77
|
tests/test_recipe_infeasibilities.py
|
|
73
78
|
tests/test_recipe_nonlinear.py
|
|
74
79
|
tests/test_recipe_piecewise.py
|
|
@@ -51,9 +51,9 @@ AARCH=$(uname -m)
|
|
|
51
51
|
echo "------"
|
|
52
52
|
echo $AARCH
|
|
53
53
|
if [[ $AARCH == "aarch64" ]]; then
|
|
54
|
-
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.
|
|
54
|
+
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.9.0/libscip-linux-arm.zip -O scip.zip
|
|
55
55
|
else
|
|
56
|
-
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.
|
|
56
|
+
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.9.0/libscip-linux.zip -O scip.zip
|
|
57
57
|
fi
|
|
58
58
|
unzip scip.zip
|
|
59
59
|
mv scip_install scip
|
|
@@ -67,11 +67,11 @@ before-all = '''
|
|
|
67
67
|
#!/bin/bash
|
|
68
68
|
brew install wget zlib gcc
|
|
69
69
|
if [[ $CIBW_ARCHS == *"arm"* ]]; then
|
|
70
|
-
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.
|
|
70
|
+
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.9.0/libscip-macos-arm.zip -O scip.zip
|
|
71
71
|
export MACOSX_DEPLOYMENT_TARGET=14.0
|
|
72
72
|
else
|
|
73
|
-
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.
|
|
74
|
-
export MACOSX_DEPLOYMENT_TARGET=
|
|
73
|
+
wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.9.0/libscip-macos-intel.zip -O scip.zip
|
|
74
|
+
export MACOSX_DEPLOYMENT_TARGET=14.0
|
|
75
75
|
fi
|
|
76
76
|
unzip scip.zip
|
|
77
77
|
mv scip_install src/scip
|
|
@@ -84,7 +84,7 @@ repair-wheel-command = '''
|
|
|
84
84
|
delocate-listdeps {wheel}
|
|
85
85
|
delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}
|
|
86
86
|
else
|
|
87
|
-
export MACOSX_DEPLOYMENT_TARGET=
|
|
87
|
+
export MACOSX_DEPLOYMENT_TARGET=14.0
|
|
88
88
|
delocate-listdeps {wheel}
|
|
89
89
|
delocate-wheel --require-archs {delocate_archs} -w {dest_dir} {wheel}
|
|
90
90
|
fi
|
|
@@ -96,7 +96,7 @@ repair-wheel-command = '''
|
|
|
96
96
|
skip="pp* cp36* cp37*"
|
|
97
97
|
before-all = [
|
|
98
98
|
"choco install 7zip wget",
|
|
99
|
-
"wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.
|
|
99
|
+
"wget https://github.com/scipopt/scipoptsuite-deploy/releases/download/v0.9.0/libscip-windows.zip -O scip.zip",
|
|
100
100
|
"\"C:\\Program Files\\7-Zip\\7z.exe\" x \"scip.zip\" -o\"scip-test\"",
|
|
101
101
|
"mv .\\scip-test\\scip_install .\\test",
|
|
102
102
|
"mv .\\test .\\scip"
|
|
@@ -7,54 +7,63 @@ scipoptdir = os.environ.get("SCIPOPTDIR", "").strip('"')
|
|
|
7
7
|
extra_compile_args = []
|
|
8
8
|
extra_link_args = []
|
|
9
9
|
|
|
10
|
-
# if SCIPOPTDIR is not set,
|
|
10
|
+
# if SCIPOPTDIR is not set, try to detect conda environment, otherwise assume global installation
|
|
11
11
|
if not scipoptdir:
|
|
12
|
-
if
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
# check if we're in a conda environment
|
|
13
|
+
conda_prefix = os.environ.get("CONDA_PREFIX", "").strip('"')
|
|
14
|
+
|
|
15
|
+
if conda_prefix and os.path.exists(os.path.join(conda_prefix, "include")):
|
|
16
|
+
includedir = os.path.join(conda_prefix, "include")
|
|
17
|
+
libdir = os.path.join(conda_prefix, "lib")
|
|
18
|
+
libname = "libscip" if platform.system() == "Windows" else "scip"
|
|
19
|
+
print(f"Detected conda environment at {conda_prefix}.")
|
|
20
|
+
print(f"Using include path {includedir}.")
|
|
21
|
+
print(f"Using library directory {libdir}.\n")
|
|
15
22
|
else:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
# fall back to global installation
|
|
24
|
+
if platform.system() == "Darwin":
|
|
25
|
+
includedirs = ["/usr/local/include"]
|
|
26
|
+
libdir = "/usr/local/lib"
|
|
27
|
+
else:
|
|
28
|
+
includedirs = ["."]
|
|
29
|
+
libdir = "."
|
|
30
|
+
libname = "libscip" if platform.system() == "Windows" else "scip"
|
|
31
|
+
print("Assuming that SCIP is installed globally, because SCIPOPTDIR is undefined.\n")
|
|
20
32
|
|
|
21
33
|
else:
|
|
22
34
|
|
|
23
35
|
# check whether SCIP is installed in the given directory
|
|
24
36
|
if os.path.exists(os.path.join(scipoptdir, "include")):
|
|
25
|
-
|
|
37
|
+
includedirs = [os.path.abspath(os.path.join(scipoptdir, "include"))]
|
|
26
38
|
else:
|
|
27
|
-
print(
|
|
39
|
+
print("SCIPOPTDIR=%s does not contain an include directory; searching for include files in src or ../src directory.\n" % scipoptdir)
|
|
28
40
|
|
|
29
41
|
if os.path.exists(os.path.join(scipoptdir, "src")):
|
|
30
42
|
# SCIP seems to be installed in-place; check whether it was built using make or cmake
|
|
31
43
|
if os.path.exists(os.path.join(scipoptdir, "src", "scip")):
|
|
32
44
|
# assume that SCIPOPTDIR pointed to the main source directory (make)
|
|
33
|
-
|
|
45
|
+
includedirs = [os.path.abspath(os.path.join(scipoptdir, "src")), os.path.abspath(os.path.join(scipoptdir, "lib/shared/include"))]
|
|
34
46
|
else:
|
|
35
47
|
# assume that SCIPOPTDIR pointed to a cmake build directory; try one level up (this is just a heuristic)
|
|
36
48
|
if os.path.exists(os.path.join(scipoptdir, "..", "src", "scip")):
|
|
37
|
-
|
|
49
|
+
includedirs = [os.path.abspath(os.path.join(scipoptdir, "..", "src"))]
|
|
38
50
|
else:
|
|
39
|
-
sys.exit(
|
|
51
|
+
sys.exit("Could neither find src/scip nor ../src/scip directory in SCIPOPTDIR=%s. Consider installing SCIP in a separate directory." % scipoptdir)
|
|
40
52
|
else:
|
|
41
|
-
sys.exit(
|
|
53
|
+
sys.exit("Could not find a src directory in SCIPOPTDIR=%s; maybe it points to a wrong directory." % scipoptdir)
|
|
42
54
|
|
|
43
55
|
# determine library
|
|
44
56
|
if os.path.exists(os.path.join(scipoptdir, "lib", "shared", "libscip.so")):
|
|
45
57
|
# SCIP seems to be created with make
|
|
46
58
|
libdir = os.path.abspath(os.path.join(scipoptdir, "lib", "shared"))
|
|
47
59
|
libname = "scip"
|
|
48
|
-
extra_compile_args.append("-DNO_CONFIG_HEADER")
|
|
49
|
-
# the following is a temporary hack to make it compile with SCIP/make:
|
|
50
|
-
extra_compile_args.append("-DTPI_NONE") # if other TPIs are used, please modify
|
|
51
60
|
else:
|
|
52
61
|
# assume that SCIP is installed on the system
|
|
53
62
|
libdir = os.path.abspath(os.path.join(scipoptdir, "lib"))
|
|
54
|
-
libname = "libscip" if platform.system()
|
|
63
|
+
libname = "libscip" if platform.system() == "Windows" else "scip"
|
|
55
64
|
|
|
56
|
-
print(
|
|
57
|
-
print(
|
|
65
|
+
print("Using include path %s." % includedirs)
|
|
66
|
+
print("Using SCIP library %s at %s.\n" % (libname, libdir))
|
|
58
67
|
|
|
59
68
|
# set runtime libraries
|
|
60
69
|
if platform.system() in ["Linux", "Darwin"]:
|
|
@@ -84,7 +93,6 @@ if not os.path.exists(os.path.join(packagedir, "scip.pyx")):
|
|
|
84
93
|
|
|
85
94
|
ext = ".pyx" if use_cython else ".c"
|
|
86
95
|
|
|
87
|
-
|
|
88
96
|
on_github_actions = os.getenv('GITHUB_ACTIONS') == 'true'
|
|
89
97
|
release_mode = os.getenv('RELEASE') == 'true'
|
|
90
98
|
compile_with_line_tracing = on_github_actions and not release_mode
|
|
@@ -92,8 +100,8 @@ compile_with_line_tracing = on_github_actions and not release_mode
|
|
|
92
100
|
extensions = [
|
|
93
101
|
Extension(
|
|
94
102
|
"pyscipopt.scip",
|
|
95
|
-
[os.path.join(packagedir,
|
|
96
|
-
include_dirs=
|
|
103
|
+
[os.path.join(packagedir, "scip%s" % ext)],
|
|
104
|
+
include_dirs=includedirs,
|
|
97
105
|
library_dirs=[libdir],
|
|
98
106
|
libraries=[libname],
|
|
99
107
|
extra_compile_args=extra_compile_args,
|
|
@@ -110,7 +118,7 @@ with open("README.md") as f:
|
|
|
110
118
|
|
|
111
119
|
setup(
|
|
112
120
|
name="PySCIPOpt",
|
|
113
|
-
version="5.
|
|
121
|
+
version="5.7.0",
|
|
114
122
|
description="Python interface and modeling environment for SCIP",
|
|
115
123
|
long_description=long_description,
|
|
116
124
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from ._version import __version__
|
|
2
|
+
|
|
3
|
+
# required for Python 3.8 on Windows
|
|
4
|
+
import os
|
|
5
|
+
if hasattr(os, 'add_dll_directory'):
|
|
6
|
+
if os.getenv('SCIPOPTDIR'):
|
|
7
|
+
os.add_dll_directory(os.path.join(os.getenv('SCIPOPTDIR').strip('"'), 'bin'))
|
|
8
|
+
|
|
9
|
+
# export user-relevant objects:
|
|
10
|
+
from pyscipopt.Multidict import multidict as multidict
|
|
11
|
+
from pyscipopt.scip import Model as Model
|
|
12
|
+
from pyscipopt.scip import Variable as Variable
|
|
13
|
+
from pyscipopt.scip import MatrixVariable as MatrixVariable
|
|
14
|
+
from pyscipopt.scip import Constraint as Constraint
|
|
15
|
+
from pyscipopt.scip import MatrixConstraint as MatrixConstraint
|
|
16
|
+
from pyscipopt.scip import Benders as Benders
|
|
17
|
+
from pyscipopt.scip import Benderscut as Benderscut
|
|
18
|
+
from pyscipopt.scip import Branchrule as Branchrule
|
|
19
|
+
from pyscipopt.scip import Nodesel as Nodesel
|
|
20
|
+
from pyscipopt.scip import Conshdlr as Conshdlr
|
|
21
|
+
from pyscipopt.scip import Eventhdlr as Eventhdlr
|
|
22
|
+
from pyscipopt.scip import Heur as Heur
|
|
23
|
+
from pyscipopt.scip import Presol as Presol
|
|
24
|
+
from pyscipopt.scip import Pricer as Pricer
|
|
25
|
+
from pyscipopt.scip import Prop as Prop
|
|
26
|
+
from pyscipopt.scip import Reader as Reader
|
|
27
|
+
from pyscipopt.scip import Sepa as Sepa
|
|
28
|
+
from pyscipopt.scip import LP as LP
|
|
29
|
+
from pyscipopt.scip import PY_SCIP_LPPARAM as SCIP_LPPARAM
|
|
30
|
+
from pyscipopt.scip import readStatistics as readStatistics
|
|
31
|
+
from pyscipopt.scip import Expr as Expr
|
|
32
|
+
from pyscipopt.scip import MatrixExpr as MatrixExpr
|
|
33
|
+
from pyscipopt.scip import MatrixExprCons as MatrixExprCons
|
|
34
|
+
from pyscipopt.scip import ExprCons as ExprCons
|
|
35
|
+
from pyscipopt.scip import quicksum as quicksum
|
|
36
|
+
from pyscipopt.scip import quickprod as quickprod
|
|
37
|
+
from pyscipopt.scip import exp as exp
|
|
38
|
+
from pyscipopt.scip import log as log
|
|
39
|
+
from pyscipopt.scip import sqrt as sqrt
|
|
40
|
+
from pyscipopt.scip import sin as sin
|
|
41
|
+
from pyscipopt.scip import cos as cos
|
|
42
|
+
from pyscipopt.scip import PY_SCIP_RESULT as SCIP_RESULT
|
|
43
|
+
from pyscipopt.scip import PY_SCIP_PARAMSETTING as SCIP_PARAMSETTING
|
|
44
|
+
from pyscipopt.scip import PY_SCIP_PARAMEMPHASIS as SCIP_PARAMEMPHASIS
|
|
45
|
+
from pyscipopt.scip import PY_SCIP_STATUS as SCIP_STATUS
|
|
46
|
+
from pyscipopt.scip import PY_SCIP_STAGE as SCIP_STAGE
|
|
47
|
+
from pyscipopt.scip import PY_SCIP_NODETYPE as SCIP_NODETYPE
|
|
48
|
+
from pyscipopt.scip import PY_SCIP_PROPTIMING as SCIP_PROPTIMING
|
|
49
|
+
from pyscipopt.scip import PY_SCIP_PRESOLTIMING as SCIP_PRESOLTIMING
|
|
50
|
+
from pyscipopt.scip import PY_SCIP_HEURTIMING as SCIP_HEURTIMING
|
|
51
|
+
from pyscipopt.scip import PY_SCIP_EVENTTYPE as SCIP_EVENTTYPE
|
|
52
|
+
from pyscipopt.scip import PY_SCIP_LOCKTYPE as SCIP_LOCKTYPE
|
|
53
|
+
from pyscipopt.scip import PY_SCIP_LPSOLSTAT as SCIP_LPSOLSTAT
|
|
54
|
+
from pyscipopt.scip import PY_SCIP_BRANCHDIR as SCIP_BRANCHDIR
|
|
55
|
+
from pyscipopt.scip import PY_SCIP_BENDERSENFOTYPE as SCIP_BENDERSENFOTYPE
|
|
56
|
+
from pyscipopt.scip import PY_SCIP_ROWORIGINTYPE as SCIP_ROWORIGINTYPE
|
|
57
|
+
from pyscipopt.scip import PY_SCIP_SOLORIGIN as SCIP_SOLORIGIN
|
|
58
|
+
from pyscipopt.scip import PY_SCIP_NODETYPE as SCIP_NODETYPE
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__: str = '5.7.0'
|
|
@@ -39,7 +39,7 @@ cdef class Eventhdlr:
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# local helper functions for the interface
|
|
42
|
-
cdef Eventhdlr getPyEventhdlr(SCIP_EVENTHDLR* eventhdlr)
|
|
42
|
+
cdef Eventhdlr getPyEventhdlr(SCIP_EVENTHDLR* eventhdlr):
|
|
43
43
|
cdef SCIP_EVENTHDLRDATA* eventhdlrdata
|
|
44
44
|
eventhdlrdata = SCIPeventhdlrGetData(eventhdlr)
|
|
45
45
|
return <Eventhdlr>eventhdlrdata
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
# Modifying the expression directly would be a bug, given that the expression might be re-used by the user. </pre>
|
|
45
45
|
include "matrix.pxi"
|
|
46
46
|
|
|
47
|
+
|
|
47
48
|
def _is_number(e):
|
|
48
49
|
try:
|
|
49
50
|
f = float(e)
|
|
@@ -52,7 +53,8 @@ def _is_number(e):
|
|
|
52
53
|
return False
|
|
53
54
|
except TypeError: # for other types (Variable, Expr)
|
|
54
55
|
return False
|
|
55
|
-
|
|
56
|
+
|
|
57
|
+
|
|
56
58
|
def _expr_richcmp(self, other, op):
|
|
57
59
|
if op == 1: # <=
|
|
58
60
|
if isinstance(other, Expr) or isinstance(other, GenExpr):
|
|
@@ -62,7 +64,7 @@ def _expr_richcmp(self, other, op):
|
|
|
62
64
|
elif isinstance(other, MatrixExpr):
|
|
63
65
|
return _expr_richcmp(other, self, 5)
|
|
64
66
|
else:
|
|
65
|
-
raise
|
|
67
|
+
raise TypeError(f"Unsupported type {type(other)}")
|
|
66
68
|
elif op == 5: # >=
|
|
67
69
|
if isinstance(other, Expr) or isinstance(other, GenExpr):
|
|
68
70
|
return (self - other) >= 0.0
|
|
@@ -71,7 +73,7 @@ def _expr_richcmp(self, other, op):
|
|
|
71
73
|
elif isinstance(other, MatrixExpr):
|
|
72
74
|
return _expr_richcmp(other, self, 1)
|
|
73
75
|
else:
|
|
74
|
-
raise
|
|
76
|
+
raise TypeError(f"Unsupported type {type(other)}")
|
|
75
77
|
elif op == 2: # ==
|
|
76
78
|
if isinstance(other, Expr) or isinstance(other, GenExpr):
|
|
77
79
|
return (self - other) == 0.0
|
|
@@ -80,7 +82,7 @@ def _expr_richcmp(self, other, op):
|
|
|
80
82
|
elif isinstance(other, MatrixExpr):
|
|
81
83
|
return _expr_richcmp(other, self, 2)
|
|
82
84
|
else:
|
|
83
|
-
raise
|
|
85
|
+
raise TypeError(f"Unsupported type {type(other)}")
|
|
84
86
|
else:
|
|
85
87
|
raise NotImplementedError("Can only support constraints with '<=', '>=', or '=='.")
|
|
86
88
|
|
|
@@ -146,7 +148,7 @@ def buildGenExprObj(expr):
|
|
|
146
148
|
GenExprs = np.empty(expr.shape, dtype=object)
|
|
147
149
|
for idx in np.ndindex(expr.shape):
|
|
148
150
|
GenExprs[idx] = buildGenExprObj(expr[idx])
|
|
149
|
-
return GenExprs
|
|
151
|
+
return GenExprs.view(MatrixExpr)
|
|
150
152
|
|
|
151
153
|
else:
|
|
152
154
|
assert isinstance(expr, GenExpr)
|
|
@@ -201,7 +203,7 @@ cdef class Expr:
|
|
|
201
203
|
elif isinstance(right, MatrixExpr):
|
|
202
204
|
return right + left
|
|
203
205
|
else:
|
|
204
|
-
raise
|
|
206
|
+
raise TypeError(f"Unsupported type {type(right)}")
|
|
205
207
|
|
|
206
208
|
return Expr(terms)
|
|
207
209
|
|
|
@@ -218,11 +220,14 @@ cdef class Expr:
|
|
|
218
220
|
# TypeError: Cannot convert pyscipopt.scip.SumExpr to pyscipopt.scip.Expr
|
|
219
221
|
return buildGenExprObj(self) + other
|
|
220
222
|
else:
|
|
221
|
-
raise
|
|
223
|
+
raise TypeError(f"Unsupported type {type(other)}")
|
|
222
224
|
|
|
223
225
|
return self
|
|
224
226
|
|
|
225
227
|
def __mul__(self, other):
|
|
228
|
+
if isinstance(other, MatrixExpr):
|
|
229
|
+
return other * self
|
|
230
|
+
|
|
226
231
|
if _is_number(other):
|
|
227
232
|
f = float(other)
|
|
228
233
|
return Expr({v:f*c for v,c in self.terms.items()})
|
|
@@ -267,6 +272,19 @@ cdef class Expr:
|
|
|
267
272
|
res *= self
|
|
268
273
|
return res
|
|
269
274
|
|
|
275
|
+
def __rpow__(self, other):
|
|
276
|
+
"""
|
|
277
|
+
Implements base**x as scip.exp(x * scip.log(base)).
|
|
278
|
+
Note: base must be positive.
|
|
279
|
+
"""
|
|
280
|
+
if _is_number(other):
|
|
281
|
+
base = float(other)
|
|
282
|
+
if base <= 0.0:
|
|
283
|
+
raise ValueError("Base of a**x must be positive, as expression is reformulated to scip.exp(x * scip.log(a)); got %g" % base)
|
|
284
|
+
return exp(self * log(base))
|
|
285
|
+
else:
|
|
286
|
+
raise TypeError(f"Unsupported base type {type(other)} for exponentiation.")
|
|
287
|
+
|
|
270
288
|
def __neg__(self):
|
|
271
289
|
return Expr({v:-c for v,c in self.terms.items()})
|
|
272
290
|
|
|
@@ -334,31 +352,31 @@ cdef class ExprCons:
|
|
|
334
352
|
def __richcmp__(self, other, op):
|
|
335
353
|
'''turn it into a constraint'''
|
|
336
354
|
if op == 1: # <=
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
355
|
+
if not self._rhs is None:
|
|
356
|
+
raise TypeError('ExprCons already has upper bound')
|
|
357
|
+
assert not self._lhs is None
|
|
340
358
|
|
|
341
|
-
|
|
342
|
-
|
|
359
|
+
if not _is_number(other):
|
|
360
|
+
raise TypeError('Ranged ExprCons is not well defined!')
|
|
343
361
|
|
|
344
|
-
|
|
362
|
+
return ExprCons(self.expr, lhs=self._lhs, rhs=float(other))
|
|
345
363
|
elif op == 5: # >=
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
364
|
+
if not self._lhs is None:
|
|
365
|
+
raise TypeError('ExprCons already has lower bound')
|
|
366
|
+
assert self._lhs is None
|
|
367
|
+
assert not self._rhs is None
|
|
350
368
|
|
|
351
|
-
|
|
352
|
-
|
|
369
|
+
if not _is_number(other):
|
|
370
|
+
raise TypeError('Ranged ExprCons is not well defined!')
|
|
353
371
|
|
|
354
|
-
|
|
372
|
+
return ExprCons(self.expr, lhs=float(other), rhs=self._rhs)
|
|
355
373
|
else:
|
|
356
|
-
raise
|
|
374
|
+
raise NotImplementedError("Ranged ExprCons can only support with '<=' or '>='.")
|
|
357
375
|
|
|
358
376
|
def __repr__(self):
|
|
359
377
|
return 'ExprCons(%s, %s, %s)' % (self.expr, self._lhs, self._rhs)
|
|
360
378
|
|
|
361
|
-
def
|
|
379
|
+
def __bool__(self):
|
|
362
380
|
'''Make sure that equality of expressions is not asserted with =='''
|
|
363
381
|
|
|
364
382
|
msg = """Can't evaluate constraints as booleans.
|
|
@@ -420,6 +438,9 @@ cdef class GenExpr:
|
|
|
420
438
|
return UnaryExpr(Operator.fabs, self)
|
|
421
439
|
|
|
422
440
|
def __add__(self, other):
|
|
441
|
+
if isinstance(other, MatrixExpr):
|
|
442
|
+
return other + self
|
|
443
|
+
|
|
423
444
|
left = buildGenExprObj(self)
|
|
424
445
|
right = buildGenExprObj(other)
|
|
425
446
|
ans = SumExpr()
|
|
@@ -475,6 +496,9 @@ cdef class GenExpr:
|
|
|
475
496
|
# return self
|
|
476
497
|
|
|
477
498
|
def __mul__(self, other):
|
|
499
|
+
if isinstance(other, MatrixExpr):
|
|
500
|
+
return other * self
|
|
501
|
+
|
|
478
502
|
left = buildGenExprObj(self)
|
|
479
503
|
right = buildGenExprObj(other)
|
|
480
504
|
ans = ProdExpr()
|
|
@@ -533,11 +557,24 @@ cdef class GenExpr:
|
|
|
533
557
|
|
|
534
558
|
return ans
|
|
535
559
|
|
|
560
|
+
def __rpow__(self, other):
|
|
561
|
+
"""
|
|
562
|
+
Implements base**x as scip.exp(x * scip.log(base)).
|
|
563
|
+
Note: base must be positive.
|
|
564
|
+
"""
|
|
565
|
+
if _is_number(other):
|
|
566
|
+
base = float(other)
|
|
567
|
+
if base <= 0.0:
|
|
568
|
+
raise ValueError("Base of a**x must be positive, as expression is reformulated to scip.exp(x * scip.log(a)); got %g" % base)
|
|
569
|
+
return exp(self * log(base))
|
|
570
|
+
else:
|
|
571
|
+
raise TypeError(f"Unsupported base type {type(other)} for exponentiation.")
|
|
572
|
+
|
|
536
573
|
#TODO: ipow, idiv, etc
|
|
537
574
|
def __truediv__(self,other):
|
|
538
575
|
divisor = buildGenExprObj(other)
|
|
539
576
|
# we can't divide by 0
|
|
540
|
-
if divisor.getOp() == Operator.const and divisor.number == 0.0:
|
|
577
|
+
if isinstance(divisor, GenExpr) and divisor.getOp() == Operator.const and divisor.number == 0.0:
|
|
541
578
|
raise ZeroDivisionError("cannot divide by 0")
|
|
542
579
|
return self * divisor**(-1)
|
|
543
580
|
|
|
@@ -58,7 +58,8 @@ cdef class LP:
|
|
|
58
58
|
"""Adds a single column to the LP.
|
|
59
59
|
|
|
60
60
|
Keyword arguments:
|
|
61
|
-
entries -- list of tuples
|
|
61
|
+
entries -- a list of tuples; if p is the index of the new column, then each tuple (i, k) indicates that
|
|
62
|
+
A[i][p] = k, where A is the constraint matrix and k is a nonzero entry.
|
|
62
63
|
obj -- objective coefficient (default 0.0)
|
|
63
64
|
lb -- lower bound (default 0.0)
|
|
64
65
|
ub -- upper bound (default infinity)
|
|
@@ -85,7 +86,8 @@ cdef class LP:
|
|
|
85
86
|
"""Adds multiple columns to the LP.
|
|
86
87
|
|
|
87
88
|
Keyword arguments:
|
|
88
|
-
entrieslist -- list
|
|
89
|
+
entrieslist -- a list of lists, where the j-th inner list contains tuples (i, k) such that A[i][p] = k,
|
|
90
|
+
where A is the constraint matrix, p is the index of the j-th new column, and k is a nonzero entry.
|
|
89
91
|
objs -- objective coefficient (default 0.0)
|
|
90
92
|
lbs -- lower bounds (default 0.0)
|
|
91
93
|
ubs -- upper bounds (default infinity)
|
|
@@ -147,7 +149,8 @@ cdef class LP:
|
|
|
147
149
|
"""Adds a single row to the LP.
|
|
148
150
|
|
|
149
151
|
Keyword arguments:
|
|
150
|
-
entries -- list of tuples, each tuple
|
|
152
|
+
entries -- a list of tuples; if q is the index of the new row, then each tuple (j, k) indicates that
|
|
153
|
+
A[q][j] = k, where A is the constraint matrix and k is a nonzero entry.
|
|
151
154
|
lhs -- left-hand side of the row (default 0.0)
|
|
152
155
|
rhs -- right-hand side of the row (default infinity)
|
|
153
156
|
"""
|
|
@@ -172,7 +175,8 @@ cdef class LP:
|
|
|
172
175
|
"""Adds multiple rows to the LP.
|
|
173
176
|
|
|
174
177
|
Keyword arguments:
|
|
175
|
-
entrieslist -- list
|
|
178
|
+
entrieslist -- a list of lists, where the i-th inner list contains tuples (j, k) such that A[q][j] = k,
|
|
179
|
+
where A is the constraint matrix, q is the index of the i-th new row, and k is a nonzero entry.
|
|
176
180
|
lhss -- left-hand side of the row (default 0.0)
|
|
177
181
|
rhss -- right-hand side of the row (default infinity)
|
|
178
182
|
"""
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# TODO Cythonize things. Improve performance.
|
|
3
|
+
# TODO Add tests
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _is_number(e):
|
|
11
|
+
try:
|
|
12
|
+
f = float(e)
|
|
13
|
+
return True
|
|
14
|
+
except ValueError: # for malformed strings
|
|
15
|
+
return False
|
|
16
|
+
except TypeError: # for other types (Variable, Expr)
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _matrixexpr_richcmp(self, other, op):
|
|
21
|
+
def _richcmp(self, other, op):
|
|
22
|
+
if op == 1: # <=
|
|
23
|
+
return self.__le__(other)
|
|
24
|
+
elif op == 5: # >=
|
|
25
|
+
return self.__ge__(other)
|
|
26
|
+
elif op == 2: # ==
|
|
27
|
+
return self.__eq__(other)
|
|
28
|
+
else:
|
|
29
|
+
raise NotImplementedError("Can only support constraints with '<=', '>=', or '=='.")
|
|
30
|
+
|
|
31
|
+
if _is_number(other) or isinstance(other, Expr):
|
|
32
|
+
res = np.empty(self.shape, dtype=object)
|
|
33
|
+
res.flat = [_richcmp(i, other, op) for i in self.flat]
|
|
34
|
+
|
|
35
|
+
elif isinstance(other, np.ndarray):
|
|
36
|
+
out = np.broadcast(self, other)
|
|
37
|
+
res = np.empty(out.shape, dtype=object)
|
|
38
|
+
res.flat = [_richcmp(i, j, op) for i, j in out]
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
raise TypeError(f"Unsupported type {type(other)}")
|
|
42
|
+
|
|
43
|
+
return res.view(MatrixExprCons)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MatrixExpr(np.ndarray):
|
|
47
|
+
def sum(self, **kwargs):
|
|
48
|
+
"""
|
|
49
|
+
Based on `numpy.ndarray.sum`, but returns a scalar if `axis=None`.
|
|
50
|
+
This is useful for matrix expressions to compare with a matrix or a scalar.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
if kwargs.get("axis") is None:
|
|
54
|
+
# Speed up `.sum()` #1070
|
|
55
|
+
return quicksum(self.flat)
|
|
56
|
+
return super().sum(**kwargs)
|
|
57
|
+
|
|
58
|
+
def __le__(self, other: Union[float, int, "Expr", np.ndarray, "MatrixExpr"]) -> MatrixExprCons:
|
|
59
|
+
return _matrixexpr_richcmp(self, other, 1)
|
|
60
|
+
|
|
61
|
+
def __ge__(self, other: Union[float, int, "Expr", np.ndarray, "MatrixExpr"]) -> MatrixExprCons:
|
|
62
|
+
return _matrixexpr_richcmp(self, other, 5)
|
|
63
|
+
|
|
64
|
+
def __eq__(self, other: Union[float, int, "Expr", np.ndarray, "MatrixExpr"]) -> MatrixExprCons:
|
|
65
|
+
return _matrixexpr_richcmp(self, other, 2)
|
|
66
|
+
|
|
67
|
+
def __add__(self, other):
|
|
68
|
+
return super().__add__(other).view(MatrixExpr)
|
|
69
|
+
|
|
70
|
+
def __iadd__(self, other):
|
|
71
|
+
return super().__iadd__(other).view(MatrixExpr)
|
|
72
|
+
|
|
73
|
+
def __mul__(self, other):
|
|
74
|
+
return super().__mul__(other).view(MatrixExpr)
|
|
75
|
+
|
|
76
|
+
def __truediv__(self, other):
|
|
77
|
+
return super().__truediv__(other).view(MatrixExpr)
|
|
78
|
+
|
|
79
|
+
def __rtruediv__(self, other):
|
|
80
|
+
return super().__rtruediv__(other).view(MatrixExpr)
|
|
81
|
+
|
|
82
|
+
def __pow__(self, other):
|
|
83
|
+
return super().__pow__(other).view(MatrixExpr)
|
|
84
|
+
|
|
85
|
+
def __sub__(self, other):
|
|
86
|
+
return super().__sub__(other).view(MatrixExpr)
|
|
87
|
+
|
|
88
|
+
def __radd__(self, other):
|
|
89
|
+
return super().__radd__(other).view(MatrixExpr)
|
|
90
|
+
|
|
91
|
+
def __rmul__(self, other):
|
|
92
|
+
return super().__rmul__(other).view(MatrixExpr)
|
|
93
|
+
|
|
94
|
+
def __rsub__(self, other):
|
|
95
|
+
return super().__rsub__(other).view(MatrixExpr)
|
|
96
|
+
|
|
97
|
+
def __matmul__(self, other):
|
|
98
|
+
return super().__matmul__(other).view(MatrixExpr)
|
|
99
|
+
|
|
100
|
+
class MatrixGenExpr(MatrixExpr):
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
class MatrixExprCons(np.ndarray):
|
|
104
|
+
|
|
105
|
+
def __le__(self, other: Union[float, int, np.ndarray]) -> MatrixExprCons:
|
|
106
|
+
return _matrixexpr_richcmp(self, other, 1)
|
|
107
|
+
|
|
108
|
+
def __ge__(self, other: Union[float, int, np.ndarray]) -> MatrixExprCons:
|
|
109
|
+
return _matrixexpr_richcmp(self, other, 5)
|
|
110
|
+
|
|
111
|
+
def __eq__(self, other):
|
|
112
|
+
raise NotImplementedError("Cannot compare MatrixExprCons with '=='.")
|
|
@@ -87,8 +87,12 @@ cdef SCIP_RETCODE PyNodeselSelect (SCIP* scip, SCIP_NODESEL* nodesel, SCIP_NODE*
|
|
|
87
87
|
nodeseldata = SCIPnodeselGetData(nodesel)
|
|
88
88
|
PyNodesel = <Nodesel>nodeseldata
|
|
89
89
|
result_dict = PyNodesel.nodeselect()
|
|
90
|
-
selected_node = <Node>
|
|
91
|
-
|
|
90
|
+
selected_node = <Node>result_dict.get("selnode", None)
|
|
91
|
+
if selected_node == None:
|
|
92
|
+
selnode[0] = NULL
|
|
93
|
+
else:
|
|
94
|
+
selnode[0] = selected_node.scip_node
|
|
95
|
+
|
|
92
96
|
return SCIP_OKAY
|
|
93
97
|
|
|
94
98
|
cdef int PyNodeselComp (SCIP* scip, SCIP_NODESEL* nodesel, SCIP_NODE* node1, SCIP_NODE* node2) noexcept with gil:
|
|
File without changes
|