Repository navigation
Expand file tree
/
Copy pathINSTALL.sh
More file actions
1182 lines (1110 loc) · 40.8 KB
/
Copy pathINSTALL.sh
File metadata and controls
1182 lines (1110 loc) · 40.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
# ------------------------------------------------------------------------------
# SYNOPSIS
# This script installs SMS++ and all its dependencies on Unix-based systems.
#
# DESCRIPTION
# This script performs the installation of SMS++ and all its dependencies
# on Unix-based systems. If not already present, it clones the smspp-project
# repositories, then builds and installs them.
#
# You can use the `--install-root=<your-custom-path>` option to specify your custom installation root.
# You can use the `--cplex-installer=<path-to-installer>` option to point to your own CPLEX installer.
# You can use the `--without-cplex` option to skip the installation of CPLEX.
# You can use the `--without-gurobi` option to skip the installation of Gurobi.
# You can use the `--without-scip` option to skip the installation of SCIP.
# You can use the `--without-highs` option to skip the installation of HiGHS.
# You can use the `--without-pips` option to skip the installation of PIPS-IPM++.
# You can use the `--without-stopt` option to skip the installation of StOpt.
# You can use the `--without-torch` option to skip the installation of Torch.
# You can use the `--without-lemon` option to skip the installation of LEMON.
# You can use the `--without-libsvm` option to skip the installation of LIBSVM.
# You can use the `--without-liblinear` option to skip the installation of LIBLINEAR.
# You can use the `--without-cadical` option to skip the installation of CaDiCaL.
# You can use the `--without-minisat` option to skip the installation of MiniSat.
# You can use the `--without-record` option to skip the installation of RECORD.
# You can use the `--without-smspp` option to skip the installation of SMS++.
#
# Skipping a dependency that an SMS++ module hard-requires automatically
# disables that module when building SMS++, so configuration does not fail
# looking for a missing library: --without-stopt disables SDDPBlock and
# InvestmentBlock, --without-lemon disables MCFLemonSolver, and
# --without-pips disables PIPSMILPSolver. CaDiCaL and MiniSat are optional
# for SATBlock, which builds the SAT solvers it finds, and RECORD is
# optional for BinaryKnapsackBlock, which builds RECORDBinaryKnapsackSolver
# if it finds it. COMBO, which BinaryKnapsackBlock can also use, is for
# academic or non-commercial use only and is not installed: whoever has
# its combo.c and combo.h gives their directory as COMBO_ROOT.
#
# AUTHOR
# Donato Meoli
#
# EXAMPLES
# If you are inside the cloned repository:
#
# sudo ./INSTALL.sh --install-root=<your-custom-path> --without-<some-dependency>
#
# If you have not yet cloned the SMS++ repository, you can run the script directly:
#
# Using `curl`:
#
# If you want to install SMS++ with all dependencies:
#
# curl -s https://gitlab.com/smspp/smspp-project/-/raw/develop/INSTALL.sh | sudo bash -s -- --install-root=<your-custom-path> --without-<some-dependency>
#
# Using `wget`:
#
# If you want to install SMS++ with all dependencies:
#
# wget -qO- https://gitlab.com/smspp/smspp-project/-/raw/develop/INSTALL.sh | sudo bash -s -- --install-root=<your-custom-path> --without-<some-dependency>
#
# Notice that on macOS all the previous commands should be run **without** the `sudo`.
#
# ------------------------------------------------------------------------------
cleanup_on_error() {
if [ -n "$CURRENT_INSTALL_FOLDER" ]; then
rm -Rf "$CURRENT_INSTALL_FOLDER"
fi
exit 1
}
# Resolve where a dependency lives (Linux only): reuse an existing install under
# the default system root (DEFAULT_ROOT) if present, otherwise install it under
# the writable INSTALL_ROOT. With an explicit --install-root the two roots
# coincide, so no default-location probing happens and it is placed there.
resolve_dep_root() {
if [ -d "${DEFAULT_ROOT}/$1" ]; then
echo "${DEFAULT_ROOT}/$1"
else
echo "${INSTALL_ROOT}/$1"
fi
}
# Extract a .zip archive ($1) into a destination directory ($2). unzip is not
# installed on every system (and cannot be apt-installed without sudo) and GNU
# tar cannot read zip, so fall back to python3, which is always available.
extract_zip() {
if command -v unzip >/dev/null 2>&1; then
unzip -q "$1" -d "$2"
else
python3 -m zipfile -e "$1" "$2"
fi
}
# Function to install dependencies on Linux
install_on_linux() {
set -e # Exit immediately if a command exits with a non-zero status
trap 'cleanup_on_error' ERR
echo "Starting the installation process on Linux..."
# CaDiCaL is built from its source unless its package is installed below
cadical_from_source=1
if [ "$HAS_SUDO" -eq 1 ]; then
# Update packages and install basic requirements
echo "Updating system and installing basic requirements..."
apt-get update -q
apt-get install -y -q build-essential clang cmake cmake-curses-gui git curl
# Install Boost libraries
echo "Installing Boost libraries..."
apt-get install -y -q libboost-dev
# Install OpenMP
echo "Installing OpenMP..."
apt-get install -y -q libomp-dev
# Install Eigen
echo "Installing Eigen..."
apt-get install -y -q libeigen3-dev
# Install NetCDF-C++
echo "Installing NetCDF-C++..."
apt-get install -y -q libnetcdf-c++4-dev
# Install LEMON
if [ "$install_lemon" -eq 1 ]; then
echo "Installing LEMON..."
apt-get install -y -q liblemon-dev
fi
# Install LIBSVM
if [ "$install_libsvm" -eq 1 ]; then
echo "Installing LIBSVM..."
apt-get install -y -q libsvm-dev
fi
# Install LIBLINEAR
if [ "$install_liblinear" -eq 1 ]; then
echo "Installing LIBLINEAR..."
apt-get install -y -q liblinear-dev
fi
# Install CaDiCaL: the package is there from Debian 12 and Ubuntu 24.04 on,
# otherwise it is built from its source below
if [ "$install_cadical" -eq 1 ]; then
echo "Installing CaDiCaL..."
if apt-get install -y -q libcadical-dev; then
cadical_from_source=0
fi
fi
fi
# Install CaDiCaL from its source, where there is no package: its own
# configure script builds it in place (src/cadical.hpp and
# build/libcadical.a), which is what SATBlock looks for in CADICAL_ROOT;
# -fPIC lets the library be linked into a shared SATBlock
if [ "$install_cadical" -eq 1 ] && [ "$cadical_from_source" -eq 1 ]; then
echo "Installing CaDiCaL from its source..."
CADICAL_ROOT="$(resolve_dep_root cadical)"
CURRENT_INSTALL_FOLDER=${CADICAL_ROOT}
if [ ! -d "$CADICAL_ROOT" ]; then
git clone --branch rel-3.0.1 --depth 1 https://github.com/arminbiere/cadical.git "$CADICAL_ROOT"
cd "$CADICAL_ROOT"
./configure -fPIC
make -j "${MAX_JOBS}"
else
echo "CaDiCaL already installed."
fi
cd "$INSTALL_ROOT"
CURRENT_INSTALL_FOLDER=""
fi
# Install MiniSat from its source: the minisat package of Debian and Ubuntu
# carries the 2010 code of minisat.se, older than the stp/minisat fork,
# the one Homebrew packages, which compiles with the recent compilers as it
# is; its CMakeLists.txt asks for CMake 2.6, which CMake 4 refuses unless
# told the policies it may assume
if [ "$install_minisat" -eq 1 ]; then
echo "Installing MiniSat..."
MINISAT_ROOT="$(resolve_dep_root minisat)"
CURRENT_INSTALL_FOLDER=${MINISAT_ROOT}
if [ ! -d "$MINISAT_ROOT" ]; then
cd "$INSTALL_ROOT"
git clone --branch releases/2.2.1 --depth 1 https://github.com/stp/minisat.git minisat-src
cd minisat-src
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_INSTALL_PREFIX="$MINISAT_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
rm -rf minisat-src
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${MINISAT_ROOT}/lib' > /etc/ld.so.conf.d/minisat.conf"
ldconfig
fi
else
echo "MiniSat already installed."
fi
cd "$INSTALL_ROOT"
CURRENT_INSTALL_FOLDER=""
fi
# Install RECORD: a single source file, which BinaryKnapsackBlock compiles
# itself, so there is nothing to build here; its repository also holds a
# large set of benchmark data, hence only source/ is checked out
if [ "$install_record" -eq 1 ]; then
echo "Installing RECORD..."
RECORD_ROOT="$(resolve_dep_root record)"
CURRENT_INSTALL_FOLDER=${RECORD_ROOT}
if [ ! -d "$RECORD_ROOT" ]; then
git clone --depth 1 --filter=blob:none --sparse https://gitlab.com/renanfernandofranco/record.git "$RECORD_ROOT"
git -C "$RECORD_ROOT" sparse-checkout set source
else
echo "RECORD already installed."
fi
cd "$INSTALL_ROOT"
CURRENT_INSTALL_FOLDER=""
fi
# Install CPLEX
if [ "$install_cplex" -eq 1 ]; then
echo "Installing CPLEX..."
CPLEX_ROOT="$(resolve_dep_root ibm/ILOG/CPLEX_Studio)"
CURRENT_INSTALL_FOLDER="${INSTALL_ROOT}/ibm"
if [ ! -d "$CPLEX_ROOT" ]; then
cd "$INSTALL_ROOT"
if [ -z "$cplex_installer" ] || [ ! -f "$cplex_installer" ]; then
echo "CPLEX installer not provided. Download IBM ILOG CPLEX Optimization"
echo "Studio yourself (e.g. through the IBM Academic Initiative) and either"
echo "install it manually or re-run with --cplex-installer=<path-to-installer>."
echo "Skipping CPLEX."
install_cplex=0
else
CPLEX_INSTALLER="$cplex_installer"
chmod u+x "$CPLEX_INSTALLER"
cat <<EOL > installer.properties
INSTALLER_UI=silent
LICENSE_ACCEPTED=TRUE
USER_INSTALL_DIR=$CPLEX_ROOT
EOL
"$CPLEX_INSTALLER" -f ./installer.properties &
wait $! # wait for CPLEX installer to finish
INSTALLER_EXIT_CODE=$?
if [ $INSTALLER_EXIT_CODE -eq 0 ]; then
rm installer.properties
export CPLEX_HOME="${CPLEX_ROOT}/cplex"
export PATH="${PATH}:${CPLEX_HOME}/bin/x86-64_linux"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${CPLEX_HOME}/lib/x86-64_linux/static_pic"
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${CPLEX_HOME}/lib/x86-64_linux/static_pic' > /etc/ld.so.conf.d/cplex.conf"
ldconfig
else
rm -R javasharedresources
fi
else
echo "CPLEX installation failed with exit code $INSTALLER_EXIT_CODE."
exit 1
fi
fi
else
echo "CPLEX already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install Gurobi
if [ "$install_gurobi" -eq 1 ]; then
echo "Installing Gurobi..."
GUROBI_ROOT="$(resolve_dep_root gurobi)"
CURRENT_INSTALL_FOLDER=${GUROBI_ROOT}
if [ ! -d "$GUROBI_ROOT" ]; then
cd "$INSTALL_ROOT"
GUROBI_INSTALLER="gurobi13.0.1_linux64.tar.gz"
curl -O "https://packages.gurobi.com/13.0/$GUROBI_INSTALLER"
tar -xzf "$GUROBI_INSTALLER"
rm "$GUROBI_INSTALLER"
mv ./gurobi1301 "$GUROBI_ROOT"
export GUROBI_HOME="${GUROBI_ROOT}/linux64"
export PATH="${PATH}:${GUROBI_HOME}/bin"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${GUROBI_HOME}/lib"
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${GUROBI_HOME}/lib' > /etc/ld.so.conf.d/gurobi.conf"
ldconfig
fi
else
echo "Gurobi already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install SCIP
if [ "$install_scip" -eq 1 ]; then
echo "Installing SCIP..."
SCIP_ROOT="$(resolve_dep_root scip)"
CURRENT_INSTALL_FOLDER=${SCIP_ROOT}
if [ ! -d "$SCIP_ROOT" ]; then
if [ "$HAS_SUDO" -eq 1 ]; then
apt-get install -y -q gfortran libtbb-dev
fi
cd "$INSTALL_ROOT"
SCIP_INSTALLER="scip-9.2.1"
curl -O "https://www.scipopt.org/download/release/$SCIP_INSTALLER.tgz"
tar -xzf "$SCIP_INSTALLER.tgz"
rm "$SCIP_INSTALLER.tgz"
mv ./"$SCIP_INSTALLER" "$SCIP_ROOT"
cd "$SCIP_ROOT"
# Install PaPILO
git clone https://github.com/scipopt/papilo.git
cd papilo
cmake -S . -B build -DCMAKE_INSTALL_PREFIX="${SCIP_ROOT}/papilo"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$SCIP_ROOT"
scip_build_flags=(
"-DCMAKE_INSTALL_PREFIX=${SCIP_ROOT}"
"-DAUTOBUILD=ON"
"-DZIMPL=OFF"
"-DPAPILO_DIR=${SCIP_ROOT}/papilo"
"-DTBB=ON"
"-DOPENMP=ON"
"-Wno-dev"
)
cmake -S . -B build "${scip_build_flags[@]}"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${SCIP_ROOT}/lib' > /etc/ld.so.conf.d/scip.conf"
ldconfig
fi
else
echo "SCIP already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install HiGHS
# HIPO=ON builds the HiPO interior point solver (HiGHS >= 1.14), which is
# the only HiGHS solver able to handle convex QPs reliably; it needs a
# system BLAS.
if [ "$install_highs" -eq 1 ]; then
echo "Installing HiGHS..."
HiGHS_ROOT="$(resolve_dep_root HiGHS)"
CURRENT_INSTALL_FOLDER=${HiGHS_ROOT}
if [ "$HAS_SUDO" -eq 1 ]; then
apt-get install -y -q libopenblas-dev
fi
if [ ! -d "$HiGHS_ROOT" ]; then
cd "$INSTALL_ROOT"
git clone https://github.com/ERGO-Code/HiGHS.git
cd HiGHS
# BLA_VENDOR routes CMake to the system libopenblas: Ubuntu's
# OpenBLASConfig.cmake advertises a lib/libopenblas.so path that
# does not exist
cmake -S . -B build -DFAST_BUILD=ON -DHIPO=ON -DBLA_VENDOR=OpenBLAS -DCMAKE_INSTALL_PREFIX="$HiGHS_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${HiGHS_ROOT}/lib' > /etc/ld.so.conf.d/highs.conf"
ldconfig
fi
else
if [ "$HAS_SUDO" -eq 1 ]; then
cd "$HiGHS_ROOT"
git remote update
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse @{u})
# if the repository is not up to date
if [ "$LOCAL" != "$REMOTE" ]; then
git pull
cmake -S . -B build -DFAST_BUILD=ON -DHIPO=ON -DCMAKE_INSTALL_PREFIX="$HiGHS_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
else
echo "HiGHS already up to date."
fi
fi
fi
cd "$INSTALL_ROOT"
CURRENT_INSTALL_FOLDER=""
fi
# Install PIPS-IPM++
# PIPS-IPM++ is consumed from its build tree (no install step); the fully
# open-source build uses MUMPS as inner linear solver.
if [ "$install_pips" -eq 1 ]; then
echo "Installing PIPS-IPM++..."
PIPS_ROOT_DIR="$(resolve_dep_root pips-ipmpp)"
CURRENT_INSTALL_FOLDER=${PIPS_ROOT_DIR}
if [ ! -d "$PIPS_ROOT_DIR" ]; then
if [ "$HAS_SUDO" -eq 1 ]; then
apt-get install -y -q libopenmpi-dev gfortran liblapack-dev libmetis-dev libmumps-dev
fi
cd "$INSTALL_ROOT"
git clone --recurse-submodules https://gitlab.com/pips-ipmpp/pips-ipmpp.git "$PIPS_ROOT_DIR"
cmake -S "$PIPS_ROOT_DIR" -B "$PIPS_ROOT_DIR/build" -DCMAKE_BUILD_TYPE=Release
cmake --build "$PIPS_ROOT_DIR/build" -j "${MAX_JOBS}"
else
echo "PIPS-IPM++ already installed."
fi
cd "$INSTALL_ROOT"
CURRENT_INSTALL_FOLDER=""
fi
# Install StOpt
if [ "$install_stopt" -eq 1 ]; then
echo "Installing StOpt..."
StOpt_ROOT="$(resolve_dep_root StOpt)"
CURRENT_INSTALL_FOLDER=${StOpt_ROOT}
if [ "$HAS_SUDO" -eq 1 ]; then
# StOpt looks for BZip2 too, which the COIN-OR packages used to bring
apt-get install -y -q zlib1g-dev libbz2-dev libboost-timer-dev libboost-random-dev libboost-mpi-dev
fi
if [ ! -d "$StOpt_ROOT" ]; then
cd "$INSTALL_ROOT"
git clone https://gitlab.com/stochastic-control/StOpt.git
cd StOpt
cmake -S . -B build \
-DBUILD_PYTHON=OFF \
-DBUILD_TEST=OFF \
-DCMAKE_INSTALL_PREFIX="$StOpt_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${StOpt_ROOT}/lib' > /etc/ld.so.conf.d/stopt.conf"
ldconfig
fi
else
if [ "$HAS_SUDO" -eq 1 ]; then
cd "$StOpt_ROOT"
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse @{u})
# if the repository is not up to date
if [ "$LOCAL" != "$REMOTE" ]; then
git pull
cmake -S . -B build \
-DBUILD_PYTHON=OFF \
-DBUILD_TEST=OFF \
-DCMAKE_INSTALL_PREFIX="$StOpt_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
else
echo "StOpt already up to date."
fi
cd "$INSTALL_ROOT"
fi
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install Torch
if [ "$install_torch" -eq 1 ]; then
echo "Installing Torch..."
Torch_ROOT="$(resolve_dep_root torch)"
CURRENT_INSTALL_FOLDER=${Torch_ROOT}
if [ ! -d "$Torch_ROOT" ]; then
cd "$INSTALL_ROOT"
TORCH_VERSION="2.12.0"
TORCH_INSTALLER="libtorch-shared-with-deps-${TORCH_VERSION}%2Bcpu.zip"
# -f fails on HTTP errors (no error page saved as a fake zip),
# -L follows download.pytorch.org's redirect
curl -fL -O "https://download.pytorch.org/libtorch/cpu/$TORCH_INSTALLER"
extract_zip "$TORCH_INSTALLER" .
rm "$TORCH_INSTALLER"
# the archive unpacks as libtorch, rename to the version-less torch
mv ./libtorch "$Torch_ROOT"
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${Torch_ROOT}/lib"
if [ "$HAS_SUDO" -eq 1 ]; then
sh -c "echo '${Torch_ROOT}/lib' > /etc/ld.so.conf.d/torch.conf"
ldconfig
fi
else
echo "Torch already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
echo "Installation completed successfully on Linux."
}
# Function to install dependencies on macOS
install_on_macos() {
set -e # Exit immediately if a command exits with a non-zero status
trap 'cleanup_on_error' ERR
echo "Starting the installation process on macOS..."
# Store arch details
if [ "$(uname -m)" == "x86_64" ]; then # Intel arch
OSX_ARCH="x86-64_osx"
else # Apple Silicon MX arch
OSX_ARCH="arm64_osx"
fi
# Install Homebrew
if command -v brew >/dev/null 2>&1; then
echo "Homebrew already installed."
else
echo "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
fi
# Install Xcode Command Line Tools (includes build-essential and clang)
if xcode-select -p >/dev/null 2>&1; then
echo "Xcode Command Line Tools already installed."
else
echo "Installing Xcode Command Line Tools..."
xcode-select --install
fi
# Install basic requirements
echo "Installing basic requirements..."
brew install bash cmake git
# Install OpenMP
echo "Installing OpenMP..."
brew install libomp
# Install Boost libraries
echo "Installing Boost libraries..."
brew install boost
# Install Eigen
echo "Installing Eigen..."
brew install eigen
# Install NetCDF
echo "Installing NetCDF..."
brew install hdf5 netcdf netcdf-cxx
# Install LEMON
# NOTE: Homebrew core has no graph-LEMON ("brew install lemon" is the LALR
# parser generator) and the community taps are unmaintained, so we use the
# MacPorts port "coinor-liblemon". MacPorts is bootstrapped from source only
# when genuinely absent (Xcode Command Line Tools, installed above, are its
# only prereq); when LEMON is already present the whole step is skipped, so
# no recompilation and no sudo prompt on repeated runs.
if [ "$install_lemon" -eq 1 ]; then
echo "Installing LEMON..."
# Surface an already-installed MacPorts even if /opt/local/bin is off PATH
# (otherwise it would be re-bootstrapped from source on every run).
if ! command -v port >/dev/null 2>&1 && [ -x /opt/local/bin/port ]; then
export PATH="/opt/local/bin:/opt/local/sbin:${PATH}"
fi
if [ -d /opt/local/include/lemon ] || [ -d /usr/local/include/lemon ]; then
echo "LEMON already installed; skipping."
else
# Bootstrap MacPorts from source only when it is genuinely absent.
if ! command -v port >/dev/null 2>&1; then
echo "MacPorts not found. Bootstrapping from source..."
MACPORTS_SRC="${INSTALL_ROOT}/macports-base"
if [ ! -d "$MACPORTS_SRC" ]; then
git clone --depth 1 https://github.com/macports/macports-base.git "$MACPORTS_SRC"
fi
cd "$MACPORTS_SRC"
./configure
make
sudo make install
# MacPorts installs into /opt/local; make port visible for the rest of run
export PATH="/opt/local/bin:/opt/local/sbin:${PATH}"
sudo port -v selfupdate
fi
sudo port install coinor-liblemon
fi
fi
# Install LIBSVM
if [ "$install_libsvm" -eq 1 ]; then
echo "Installing LIBSVM..."
brew install libsvm
fi
# Install LIBLINEAR
if [ "$install_liblinear" -eq 1 ]; then
echo "Installing LIBLINEAR..."
brew install liblinear
fi
# Install CaDiCaL
if [ "$install_cadical" -eq 1 ]; then
echo "Installing CaDiCaL..."
brew install cadical
fi
# Install MiniSat
if [ "$install_minisat" -eq 1 ]; then
echo "Installing MiniSat..."
brew install minisat
fi
# Install RECORD: a single source file, which BinaryKnapsackBlock compiles
# itself; only source/ of its repository is checked out
if [ "$install_record" -eq 1 ]; then
echo "Installing RECORD..."
RECORD_ROOT="${INSTALL_ROOT}/record"
if [ ! -d "$RECORD_ROOT" ]; then
git clone --depth 1 --filter=blob:none --sparse https://gitlab.com/renanfernandofranco/record.git "$RECORD_ROOT"
git -C "$RECORD_ROOT" sparse-checkout set source
else
echo "RECORD already installed."
fi
fi
# Install CPLEX
if [ "$install_cplex" -eq 1 ]; then
echo "Installing CPLEX..."
CPLEX_ROOT="${INSTALL_ROOT}/CPLEX_Studio"
CURRENT_INSTALL_FOLDER=${CPLEX_ROOT}
if [ ! -d "$CPLEX_ROOT" ]; then
cd "$INSTALL_ROOT"
if [ -z "$cplex_installer" ] || [ ! -f "$cplex_installer" ]; then
echo "CPLEX installer not provided. Download IBM ILOG CPLEX Optimization"
echo "Studio yourself (e.g. through the IBM Academic Initiative) and either"
echo "install it manually or re-run with --cplex-installer=<path-to-installer>."
echo "Skipping CPLEX."
install_cplex=0
else
# Create a temporary directory to extract the files
TEMP_DIR="/tmp/cplex_install"
mkdir -p "$TEMP_DIR"
# Extract directly into the temporary directory
sudo unzip "$cplex_installer" -d "$TEMP_DIR"
# Locate the extracted installer app, whose name embeds the CPLEX version
CPLEX_APP=$(find "$TEMP_DIR" -maxdepth 1 -name '*.app' | head -n 1)
CPLEX_NAME=$(basename "${CPLEX_APP%.app}")
# Remove macOS quarantine attribute to allow execution of unsigned app
sudo xattr -r -d com.apple.quarantine "$CPLEX_APP"
# Launch the installer
sudo "${CPLEX_APP}/Contents/MacOS/${CPLEX_NAME}" &
wait $! # wait for the installer to finish
INSTALLER_EXIT_CODE=$?
if [ $INSTALLER_EXIT_CODE -eq 0 ]; then
sudo rm -Rf "$TEMP_DIR"
# the installer creates /Applications/CPLEX_Studio<ver>
CPLEX_INSTALL_DIR=$(find /Applications -maxdepth 1 -type d -name 'CPLEX_Studio*' | head -n 1)
sudo mv "$CPLEX_INSTALL_DIR" "$CPLEX_ROOT"
export CPLEX_HOME="${CPLEX_ROOT}/cplex"
export PATH="${PATH}:${CPLEX_HOME}/bin/${OSX_ARCH}"
export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:${CPLEX_HOME}/lib/${OSX_ARCH}/static_pic"
else
echo "CPLEX installation failed with exit code $INSTALLER_EXIT_CODE."
exit 1
fi
fi
else
echo "CPLEX already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install Gurobi
if [ "$install_gurobi" -eq 1 ]; then
echo "Installing Gurobi..."
GUROBI_ROOT="${INSTALL_ROOT}/gurobi"
CURRENT_INSTALL_FOLDER=${GUROBI_ROOT}
if [ ! -d "$GUROBI_ROOT" ]; then
cd "$INSTALL_ROOT"
GUROBI_INSTALLER="gurobi13.0.1_macos_universal2.pkg"
curl -O "https://packages.gurobi.com/13.0/$GUROBI_INSTALLER"
sudo installer -pkg "$GUROBI_INSTALLER" -target /
rm "$GUROBI_INSTALLER"
sudo mv /Library/gurobi1301 "$GUROBI_ROOT"
export GUROBI_HOME="${GUROBI_ROOT}/macos_universal2"
export PATH="${PATH}:${GUROBI_HOME}/bin"
export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:${GUROBI_HOME}/lib"
# Fix non-default installation
if [ "$INSTALL_ROOT" != "/Library" ]; then
GUROBI_VERSION=$(ls "${GUROBI_HOME}/lib" | grep -E '^libgurobi[0-9]+\.dylib$' | sed -E 's/^libgurobi([0-9]+)\.dylib$/\1/' | head -n1)
install_name_tool -id "${GUROBI_HOME}/lib/libgurobi${GUROBI_VERSION}.dylib" "${GUROBI_HOME}/lib/libgurobi${GUROBI_VERSION}.dylib"
codesign -s - -f "${GUROBI_HOME}/lib/libgurobi${GUROBI_VERSION}.dylib" "${GUROBI_HOME}/lib/libgurobi${GUROBI_VERSION}.dylib"
fi
else
echo "Gurobi already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install SCIP
if [ "$install_scip" -eq 1 ]; then
echo "Installing SCIP..."
SCIP_ROOT="${INSTALL_ROOT}/scip"
CURRENT_INSTALL_FOLDER=${SCIP_ROOT}
if [ ! -d "$SCIP_ROOT" ]; then
brew install gcc tbb
cd "$INSTALL_ROOT"
SCIP_INSTALLER="scip-9.2.1"
curl -O "https://www.scipopt.org/download/release/$SCIP_INSTALLER.tgz"
tar -xzf "$SCIP_INSTALLER.tgz"
rm "$SCIP_INSTALLER.tgz"
sudo mv ./"$SCIP_INSTALLER" "$SCIP_ROOT"
cd "$SCIP_ROOT"
# Install PaPILO
git clone https://github.com/scipopt/papilo.git
cd papilo
cmake -S . -B build -DCMAKE_INSTALL_PREFIX="${SCIP_ROOT}/papilo"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$SCIP_ROOT"
scip_build_flags=(
"-DCMAKE_INSTALL_PREFIX=${SCIP_ROOT}"
"-DAUTOBUILD=ON"
"-DZIMPL=OFF"
"-DPAPILO_DIR=${SCIP_ROOT}/papilo"
"-DTBB=ON"
"-DOPENMP=ON"
"-Wno-dev"
)
cmake -S . -B build "${scip_build_flags[@]}"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
export PATH="${PATH}:${SCIP_ROOT}/bin"
export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:${SCIP_ROOT}/lib"
else
echo "SCIP already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install HiGHS
# HIPO=ON builds the HiPO interior point solver (HiGHS >= 1.14), which is
# the only HiGHS solver able to handle convex QPs reliably; on macOS the
# BLAS is the Accelerate framework, so no extra dependency is needed.
if [ "$install_highs" -eq 1 ]; then
echo "Installing HiGHS..."
HiGHS_ROOT="${INSTALL_ROOT}/HiGHS"
CURRENT_INSTALL_FOLDER=${HiGHS_ROOT}
if [ ! -d "$HiGHS_ROOT" ]; then
cd "$INSTALL_ROOT"
git clone https://github.com/ERGO-Code/HiGHS.git
cd HiGHS
cmake -S . -B build -DFAST_BUILD=ON -DHIPO=ON -DCMAKE_INSTALL_PREFIX="$HiGHS_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
export PATH="${PATH}:${HiGHS_ROOT}/bin"
export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:${HiGHS_ROOT}/lib"
else
cd "$HiGHS_ROOT"
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse @{u})
# if the repository is not up to date
if [ "$LOCAL" != "$REMOTE" ]; then
git pull
cmake -S . -B build -DFAST_BUILD=ON -DHIPO=ON -DCMAKE_INSTALL_PREFIX="$HiGHS_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
else
echo "HiGHS already up to date."
fi
cd "$INSTALL_ROOT"
fi
CURRENT_INSTALL_FOLDER=""
fi
# PIPS-IPM++ is not supported on macOS
if [ "$install_pips" -eq 1 ]; then
echo "Skipping PIPS-IPM++ (Linux only)."
install_pips=0
fi
# Install StOpt
if [ "$install_stopt" -eq 1 ]; then
echo "Installing StOpt..."
StOpt_ROOT="${INSTALL_ROOT}/StOpt"
CURRENT_INSTALL_FOLDER=${StOpt_ROOT}
if [ ! -d "$StOpt_ROOT" ]; then
brew install zlib boost-mpi
cd "$INSTALL_ROOT"
git clone https://gitlab.com/stochastic-control/StOpt.git
cd StOpt
cmake -S . -B build \
-DBUILD_PYTHON=OFF \
-DBUILD_TEST=OFF \
-DCMAKE_INSTALL_PREFIX="$StOpt_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
cd "$INSTALL_ROOT"
else
cd "$StOpt_ROOT"
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse @{u})
# if the repository is not up to date
if [ "$LOCAL" != "$REMOTE" ]; then
git pull
cmake -S . -B build \
-DBUILD_PYTHON=OFF \
-DBUILD_TEST=OFF \
-DCMAKE_INSTALL_PREFIX="$StOpt_ROOT"
cmake --build build -j "${MAX_JOBS}"
cmake --install build
else
echo "StOpt already up to date."
fi
cd "$INSTALL_ROOT"
fi
CURRENT_INSTALL_FOLDER=""
fi
# Install Torch
if [ "$install_torch" -eq 1 ]; then
echo "Installing Torch..."
Torch_ROOT="${INSTALL_ROOT}/torch"
CURRENT_INSTALL_FOLDER=${Torch_ROOT}
if [ ! -d "$Torch_ROOT" ]; then
cd "$INSTALL_ROOT"
TORCH_VERSION="2.12.0"
if [ "$(uname -m)" == "x86_64" ]; then # Intel arch
# no prebuilt libtorch for macOS x86_64 past 2.2.2; Torch is optional
echo "No prebuilt libtorch for macOS x86_64; skipping Torch."
else # Apple Silicon arch
TORCH_INSTALLER="libtorch-macos-arm64-${TORCH_VERSION}.zip"
# -f fails on HTTP errors (no error page saved as a fake zip),
# -L follows download.pytorch.org's redirect
curl -fL -O "https://download.pytorch.org/libtorch/cpu/$TORCH_INSTALLER"
extract_zip "$TORCH_INSTALLER" .
rm "$TORCH_INSTALLER"
# the archive unpacks as libtorch, rename to the version-less torch
mv ./libtorch "$Torch_ROOT"
export DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}:${Torch_ROOT}/lib"
fi
else
echo "Torch already installed."
fi
CURRENT_INSTALL_FOLDER=""
fi
echo "Installation completed successfully on macOS."
}
# Default values indicating if dependencies should be installed
# it works even if you use `install_*=0`
install_cplex=${install_cplex:-1}
install_gurobi=${install_gurobi:-1}
install_scip=${install_scip:-1}
install_highs=${install_highs:-1}
install_pips=${install_pips:-1}
install_stopt=${install_stopt:-1}
install_torch=${install_torch:-1}
install_lemon=${install_lemon:-1}
install_libsvm=${install_libsvm:-1}
install_liblinear=${install_liblinear:-1}
install_cadical=${install_cadical:-1}
install_minisat=${install_minisat:-1}
install_record=${install_record:-1}
install_smspp=${install_smspp:-1}
# Default value for installation root
install_root=""
# Default value for the path to a user-provided CPLEX installer
cplex_installer=""
# Default value for the maximum number of jobs
if [ -z "${MAX_JOBS:-}" ]; then
if command -v nproc >/dev/null 2>&1; then
MAX_JOBS=$(nproc)
elif [ "$(uname)" = "Darwin" ]; then
MAX_JOBS=$(sysctl -n hw.ncpu)
else
MAX_JOBS=1
fi
# each compiler process may take up to about 1.5 GB on the heaviest SMS++
# sources, so the jobs are also bounded by the available memory, lest the
# kernel kills the compiler (c++: fatal error: Killed signal terminated
# program cc1plus); an explicit MAX_JOBS in the environment is left alone
MEM_PER_JOB_MB=1536
if [ -r /proc/meminfo ]; then
MEM_MB=$(awk '/^MemAvailable:/ { print int($2 / 1024) }' /proc/meminfo)
elif [ "$(uname)" = "Darwin" ]; then
MEM_MB=$(( $(sysctl -n hw.memsize) / 1048576 ))
fi
if [ -n "${MEM_MB:-}" ]; then
MEM_JOBS=$(( MEM_MB / MEM_PER_JOB_MB ))
[ "$MEM_JOBS" -lt 1 ] && MEM_JOBS=1
[ "$MEM_JOBS" -lt "$MAX_JOBS" ] && MAX_JOBS=$MEM_JOBS
fi
fi
echo "Building with ${MAX_JOBS} parallel jobs (set MAX_JOBS to change it)"
# Parse command line arguments
for arg in "$@"
do
case $arg in
--without-cplex)
install_cplex=0
shift
;;
--without-gurobi)
install_gurobi=0
shift
;;
--without-scip)
install_scip=0
shift
;;
--without-highs)
install_highs=0
shift
;;
--without-pips)
install_pips=0
shift
;;
--without-stopt)
install_stopt=0
shift
;;
--without-torch)
install_torch=0
shift
;;
--without-lemon)
install_lemon=0
shift
;;
--without-libsvm)
install_libsvm=0
shift
;;
--without-liblinear)
install_liblinear=0
shift
;;
--without-cadical)
install_cadical=0
shift
;;
--without-minisat)
install_minisat=0
shift
;;
--without-record)
install_record=0
shift
;;
--without-coinor)
# accepted for compatibility: nothing in SMS++ depends on COIN-OR
shift
;;
--without-smspp)
install_smspp=0
shift
;;
--install-root=*)
install_root="${arg#*=}"
shift
;;
--cplex-installer=*)
cplex_installer="${arg#*=}"
shift
;;
*)
;;
esac
done
# Remove trailing slash from install_root if present
install_root="${install_root%/}"
# Detect operating system and execute the appropriate installation function
OS="$(uname)"
case "$OS" in
"Linux")
if [ -f /etc/os-release ]; then
. /etc/os-release
if [ "$ID" = "ubuntu" ] || [ "$ID" = "debian" ]; then
# Default system location where dependencies may already be installed
DEFAULT_ROOT="/opt"
# Check if the user has sudo access
if sudo -n true 2>/dev/null; then
HAS_SUDO=1
else
HAS_SUDO=0
fi
# Where to install dependencies that are not already present: an explicit
# --install-root wins (and disables default-location probing, placing
# everything there directly); otherwise the system default with sudo, or
# the user's home without it (each dependency still reused from the
# default location if found there)
if [ -n "$install_root" ]; then
INSTALL_ROOT="$install_root"
DEFAULT_ROOT="$install_root"
elif [ "$HAS_SUDO" -eq 1 ]; then
INSTALL_ROOT="$DEFAULT_ROOT"
else
INSTALL_ROOT="$HOME"
fi
mkdir -p "$INSTALL_ROOT"
SMSPP_ROOT="${INSTALL_ROOT}/smspp-project"
install_on_linux
else
echo "This script supports Debian-based Linux distros only."
exit 1
fi
else
echo "This script supports Debian-based Linux distros only."