Add support for Python 3.13 free-threaded build

I'm currently working on adding support for the Python 3.13 free-threaded build to projects in the scientific python ecosystem. We are tracking this work at https://github.com/Quansight-Labs/free-threaded-compatibility. Right now we're focusing on projects relatively low in the stack. scikit-learn isn't the lowest in the stack but it has a lot of tests that perform multithreaded workflows so running the scikit-learn tests is a productive way to elucidate threading bugs in scikit-learn and its dependencies.

Currently, scikit-learn builds fine and almost all the tests pass with the GIL disabled 🎉 

The following tests have failures:

<details>

```
FAILED sklearn/ensemble/tests/test_voting.py::test_sample_weight[42] - AssertionError:
FAILED sklearn/model_selection/tests/test_search.py::test_random_search_cv_results_multimetric - AssertionError:
FAILED sklearn/semi_supervised/tests/test_self_training.py::test_classification[threshold-base_estimator1] - AssertionError:
FAILED sklearn/semi_supervised/tests/test_self_training.py::test_classification[k_best-base_estimator1] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-linear-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-linear-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-linear-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-poly-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-poly-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-poly-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-rbf-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-rbf-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-sigmoid-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-sigmoid-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_matrix-sigmoid-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-linear-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-linear-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-linear-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-poly-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-poly-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-rbf-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-rbf-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-rbf-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-sigmoid-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-sigmoid-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[csr_array-sigmoid-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-linear-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-linear-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-poly-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-poly-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-poly-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-rbf-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-rbf-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-sigmoid-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_matrix-sigmoid-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-linear-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-linear-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-poly-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-poly-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-rbf-X_train0-y_train0-X_test0] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-rbf-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-rbf-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-sigmoid-X_train2-y_train2-X_test2] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_svc[lil_array-sigmoid-X_train3-y_train3-X_test3] - AssertionError:
FAILED sklearn/svm/tests/test_sparse.py::test_consistent_proba - AssertionError:
FAILED sklearn/svm/tests/test_svm.py::test_libsvm_iris - AssertionError:
FAILED sklearn/svm/tests/test_svm.py::test_svc_clone_with_callable_kernel - AssertionError:
FAILED sklearn/svm/tests/test_svm.py::test_consistent_proba - AssertionError:
FAILED sklearn/tests/test_build.py::test_openmp_parallelism_enabled - AssertionError:
FAILED sklearn/tests/test_pipeline.py::test_pipeline_memory - AssertionError:
```

</details>

Note that some of these failures are flaky and you may not get the same list if you run the scikit-learn tests. I also saw one deadlock in `test_calibration.py::test_parallel_execution`, so I'd also suggest running pytest with e.g. `-o fauthandler_timeout=60` set so you will get python tracebacks for all threads if any test hangs and runs longer than 60 seconds. Your mileage may vary on how fast your computer is, so 60 seconds may not be sufficient if any tests take that long to run on your system.

My plan is to go through these failures and figure out what's going wrong. Some of these may not be specific to the free-threaded build.

To run the tests with the GIL disabled, you'll need to install 3.13b1 or the latest version of the CPython 3.13 branch, build cython, numpy, and scipy from the latest version of their respective git repositories. You'll need to e.g. `pip install` pybind11 and pythran after installing numpy and before building scipy. Then build scikit-learn as an editable install and run:

```
PYTHON_GIL=0 python -m pytest sklearn
```

COMMENT:
Thanks for your inputs, I managed to get numpy, scipy, scikit-learn to build. I was able to run the full scikit-learn test suite without failures. Maybe I was just lucky, since you said some of the failures are flaky? I did try to run multiple times some of the tests you mention in your original post but I did not manage to get them to fail.

What I am seeing though is segmentation fault at exit, i.e. after the test have run.

The gdb info looks like this:
```
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
_Py_DECREF_DecRefTotal () at Objects/object.c:238
238	    reftotal_add(_PyThreadState_GET(), -1);
(gdb) bt
#0  _Py_DECREF_DecRefTotal () at Objects/object.c:238
#1  0x00007fff9129a0ef in (anonymous namespace)::._anon_193::~._anon_193() ()
   from /home/lesteve/venvs/free-threading/lib/python3.13/site-packages/scipy/_lib/_uarray/_uarray.cpython-313td-x86_64-linux-gnu.so
#2  0x00007ffff7cfefa1 in __run_exit_handlers (status=0, listp=0x7ffff7ea2680 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, 
    run_dtors=run_dtors@entry=true) at exit.c:108
#3  0x00007ffff7cff06e in __GI_exit (status=<optimized out>) at exit.c:138
#4  0x00007ffff7ce5c8f in __libc_start_call_main (main=main@entry=0x5555555d4839 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffdac8)
    at ../sysdeps/nptl/libc_start_call_main.h:74
#5  0x00007ffff7ce5d4c in __libc_start_main_impl (main=0x5555555d4839 <main>, argc=2, argv=0x7fffffffdac8, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffdab8) at ../csu/libc-start.c:360
#6  0x00005555555d4765 in _start ()
```

It seems like this happens when garbage-collecting uarray objects after a Python thread has been started, here is a reproducer with only scipy:

```py
import scipy.fft

from concurrent.futures import ThreadPoolExecutor

def noop():
    return

tpe = ThreadPoolExecutor(max_workers=1)
tpe.submit(noop).result()
```

It does not seem to be one of the known issues in Scipy https://github.com/scipy/scipy/issues/20669.

Edit: actually looking a bit further the problem still happens with `PYTHON_GIL=1`, so maybe this is more a Python 3.13 issue than a free-threading issue?

For further reference, here are the commits I used:
CPython: [6892b400dc](https://github.com/python/cpython/commits/6892b400dc) 
cython: [19f67cb1f](https://github.com/cython/cython/commits/19f67cb1f)
numpy: [1f73030](https://github.com/numpy/numpy/commits/1f73030)
scipy: [8ce393f](https://github.com/scipy/scipy/commits/8ce393f)
scikit-learn: 5cbc419b80

Labels: Enhancement, Build / CI, free-threading