Skip to content

Commit 1413c06

Browse files
authored
Run tests and build Python wheels for aarch64 architecture (#3948)
* Update setup.sh * Update test.sh * Update test_dask.py * Update test_engine.py * Update .vsts-ci.yml
1 parent d6ebd06 commit 1413c06

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
lines changed

.ci/setup.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ else # Linux
9595
cmake
9696
fi
9797
if [[ $SETUP_CONDA != "false" ]]; then
98-
curl -sL -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
98+
ARCH=$(uname -m)
99+
if [[ $ARCH == "x86_64" ]]; then
100+
curl -sL -o conda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
101+
else
102+
curl -sL -o conda.sh https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-${ARCH}.sh
103+
fi
99104
fi
100105
fi
101106

.ci/test.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,15 @@ elif [[ $TASK == "bdist" ]]; then
127127
cp dist/lightgbm-$LGB_VER-py3-none-macosx*.whl $BUILD_ARTIFACTSTAGINGDIRECTORY
128128
fi
129129
else
130-
cd $BUILD_DIRECTORY/python-package && python setup.py bdist_wheel --plat-name=manylinux1_x86_64 --python-tag py3 || exit -1
130+
ARCH=$(uname -m)
131+
if [[ $ARCH == "x86_64" ]]; then
132+
PLATFORM="manylinux1_x86_64"
133+
else
134+
PLATFORM="manylinux2014_$ARCH"
135+
fi
136+
cd $BUILD_DIRECTORY/python-package && python setup.py bdist_wheel --plat-name=$PLATFORM --python-tag py3 || exit -1
131137
if [[ $PRODUCES_ARTIFACTS == "true" ]]; then
132-
cp dist/lightgbm-$LGB_VER-py3-none-manylinux1_x86_64.whl $BUILD_ARTIFACTSTAGINGDIRECTORY
138+
cp dist/lightgbm-$LGB_VER-py3-none-$PLATFORM.whl $BUILD_ARTIFACTSTAGINGDIRECTORY
133139
fi
134140
fi
135141
pip install --user $BUILD_DIRECTORY/python-package/dist/*.whl || exit -1

.vsts-ci.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,68 @@ jobs:
138138
- bash: $(Build.SourcesDirectory)/.ci/test.sh
139139
displayName: Test
140140
###########################################
141+
- job: QEMU_multiarch
142+
###########################################
143+
variables:
144+
COMPILER: gcc
145+
OS_NAME: 'linux'
146+
PRODUCES_ARTIFACTS: 'true'
147+
pool:
148+
vmImage: ubuntu-latest
149+
timeoutInMinutes: 120
150+
strategy:
151+
matrix:
152+
bdist:
153+
TASK: bdist
154+
ARCH: aarch64
155+
steps:
156+
- script: |
157+
sudo apt-get update
158+
sudo apt-get install --no-install-recommends -y \
159+
binfmt-support \
160+
qemu \
161+
qemu-user \
162+
qemu-user-static
163+
displayName: 'Install QEMU'
164+
- script: |
165+
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
166+
displayName: 'Enable Docker multi-architecture support'
167+
- script: |
168+
export ROOT_DOCKER_FOLDER=/LightGBM
169+
cat > docker.env <<EOF
170+
AZURE=$AZURE
171+
OS_NAME=$OS_NAME
172+
COMPILER=$COMPILER
173+
TASK=$TASK
174+
METHOD=$METHOD
175+
CONDA_ENV=$CONDA_ENV
176+
PYTHON_VERSION=$PYTHON_VERSION
177+
BUILD_DIRECTORY=$ROOT_DOCKER_FOLDER
178+
LGB_VER=$(head -n 1 VERSION.txt)
179+
PRODUCES_ARTIFACTS=$PRODUCES_ARTIFACTS
180+
BUILD_ARTIFACTSTAGINGDIRECTORY=$BUILD_ARTIFACTSTAGINGDIRECTORY
181+
EOF
182+
cat > docker-script.sh <<EOF
183+
export CONDA=\$HOME/miniconda
184+
export PATH=\$CONDA/bin:\$PATH
185+
$ROOT_DOCKER_FOLDER/.ci/setup.sh || exit -1
186+
$ROOT_DOCKER_FOLDER/.ci/test.sh || exit -1
187+
EOF
188+
docker run \
189+
--rm \
190+
--env-file docker.env \
191+
-v "$(Build.SourcesDirectory)":"$ROOT_DOCKER_FOLDER" \
192+
-v "$(Build.ArtifactStagingDirectory)":"$(Build.ArtifactStagingDirectory)" \
193+
"quay.io/pypa/manylinux2014_$ARCH" \
194+
/bin/bash $ROOT_DOCKER_FOLDER/docker-script.sh
195+
displayName: 'Setup and run tests'
196+
- task: PublishBuildArtifacts@1
197+
condition: and(succeeded(), in(variables['TASK'], 'bdist'), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/')))
198+
inputs:
199+
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
200+
artifactName: PackageAssets
201+
artifactType: container
202+
###########################################
141203
- job: MacOS
142204
###########################################
143205
variables:
@@ -219,6 +281,7 @@ jobs:
219281
dependsOn:
220282
- Linux
221283
- Linux_latest
284+
- QEMU_multiarch
222285
- MacOS
223286
- Windows
224287
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/pull/')))

tests/python_package_test/test_dask.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pickle
66
import socket
77
from itertools import groupby
8+
from platform import machine
89
from os import getenv
910
from sys import platform
1011

@@ -43,7 +44,8 @@
4344

4445
pytestmark = [
4546
pytest.mark.skipif(getenv('TASK', '') == 'mpi', reason='Fails to run with MPI interface'),
46-
pytest.mark.skipif(getenv('TASK', '') == 'gpu', reason='Fails to run with GPU interface')
47+
pytest.mark.skipif(getenv('TASK', '') == 'gpu', reason='Fails to run with GPU interface'),
48+
pytest.mark.skipif(machine() != 'x86_64', reason='Fails to run with non-x86_64 architecture')
4749
]
4850

4951

tests/python_package_test/test_engine.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import math
55
import os
66
import pickle
7+
import platform
78
import random
89

910
import numpy as np
@@ -1044,15 +1045,21 @@ def test_contribs_sparse():
10441045
# convert data to dense and get back same contribs
10451046
contribs_dense = gbm.predict(X_test.toarray(), pred_contrib=True)
10461047
# validate the values are the same
1047-
np.testing.assert_allclose(contribs_csr.toarray(), contribs_dense)
1048+
if platform.machine() == 'aarch64':
1049+
np.testing.assert_allclose(contribs_csr.toarray(), contribs_dense, rtol=1, atol=1e-12)
1050+
else:
1051+
np.testing.assert_allclose(contribs_csr.toarray(), contribs_dense)
10481052
assert (np.linalg.norm(gbm.predict(X_test, raw_score=True)
10491053
- np.sum(contribs_dense, axis=1)) < 1e-4)
10501054
# validate using CSC matrix
10511055
X_test_csc = X_test.tocsc()
10521056
contribs_csc = gbm.predict(X_test_csc, pred_contrib=True)
10531057
assert isspmatrix_csc(contribs_csc)
10541058
# validate the values are the same
1055-
np.testing.assert_allclose(contribs_csc.toarray(), contribs_dense)
1059+
if platform.machine() == 'aarch64':
1060+
np.testing.assert_allclose(contribs_csc.toarray(), contribs_dense, rtol=1, atol=1e-12)
1061+
else:
1062+
np.testing.assert_allclose(contribs_csc.toarray(), contribs_dense)
10561063

10571064

10581065
def test_contribs_sparse_multiclass():
@@ -1084,7 +1091,10 @@ def test_contribs_sparse_multiclass():
10841091
contribs_csr_array = np.swapaxes(np.array([sparse_array.todense() for sparse_array in contribs_csr]), 0, 1)
10851092
contribs_csr_arr_re = contribs_csr_array.reshape((contribs_csr_array.shape[0],
10861093
contribs_csr_array.shape[1] * contribs_csr_array.shape[2]))
1087-
np.testing.assert_allclose(contribs_csr_arr_re, contribs_dense)
1094+
if platform.machine() == 'aarch64':
1095+
np.testing.assert_allclose(contribs_csr_arr_re, contribs_dense, rtol=1, atol=1e-12)
1096+
else:
1097+
np.testing.assert_allclose(contribs_csr_arr_re, contribs_dense)
10881098
contribs_dense_re = contribs_dense.reshape(contribs_csr_array.shape)
10891099
assert np.linalg.norm(gbm.predict(X_test, raw_score=True) - np.sum(contribs_dense_re, axis=2)) < 1e-4
10901100
# validate using CSC matrix
@@ -1097,7 +1107,10 @@ def test_contribs_sparse_multiclass():
10971107
contribs_csc_array = np.swapaxes(np.array([sparse_array.todense() for sparse_array in contribs_csc]), 0, 1)
10981108
contribs_csc_array = contribs_csc_array.reshape((contribs_csc_array.shape[0],
10991109
contribs_csc_array.shape[1] * contribs_csc_array.shape[2]))
1100-
np.testing.assert_allclose(contribs_csc_array, contribs_dense)
1110+
if platform.machine() == 'aarch64':
1111+
np.testing.assert_allclose(contribs_csc_array, contribs_dense, rtol=1, atol=1e-12)
1112+
else:
1113+
np.testing.assert_allclose(contribs_csc_array, contribs_dense)
11011114

11021115

11031116
@pytest.mark.skipif(psutil.virtual_memory().available / 1024 / 1024 / 1024 < 3, reason='not enough RAM')

0 commit comments

Comments
 (0)