Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 83 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ name: CI Linux
on:
push:
tags:
- '*'
- "*"
pull_request:
paths:
- '**.build'
- 'uv.lock'
- 'subprojects/**'
- '.github/workflows/ci.yml'
- "**.build"
- "uv.lock"
- "subprojects/**"
- ".github/workflows/ci.yml"
workflow_dispatch:
# Allow to run manually

jobs:
build:
linux:
name: Build and Test
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -112,10 +112,10 @@ jobs:
id: clear-container-name
if: failure() || success()
run: |
# Output the container name, but with ":" and "/" replaced by "-"
# This is needed to avoid issues with the upload-artifact action
CONTAINER_NAME=$(echo "${{ matrix.container }}" | tr ':/' '--')
echo "CONTAINER_NAME=$CONTAINER_NAME" >> $GITHUB_OUTPUT
# Output the container name, but with ":" and "/" replaced by "-"
# This is needed to avoid issues with the upload-artifact action
CONTAINER_NAME=$(echo "${{ matrix.container }}" | tr ':/' '--')
echo "CONTAINER_NAME=$CONTAINER_NAME" >> $GITHUB_OUTPUT

- name: Upload log
uses: actions/upload-artifact@v4.5.0
Expand All @@ -125,3 +125,76 @@ jobs:
path: |
build/cp313/meson-logs/
build/cp313/meson-info/

mingw:
name: Build and Test (Mingw)
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v6.7.0

- name: Set up Python
run: |
uv python install
uv venv
.venv\Scripts\activate
echo PATH=$PATH >> $GITHUB_ENV

- name: Install dependencies
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
install: >-
base-devel
mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-ninja
mingw-w64-ucrt-x86_64-gmp
mingw-w64-ucrt-x86_64-openblas
mingw-w64-ucrt-x86_64-m4ri
mingw-w64-ucrt-x86_64-flint
mingw-w64-ucrt-x86_64-mpfi
mingw-w64-ucrt-x86_64-gsl
mingw-w64-ucrt-x86_64-libgd
mingw-w64-ucrt-x86_64-gsl
mingw-w64-ucrt-x86_64-libgd
mingw-w64-ucrt-x86_64-ntl
mingw-w64-ucrt-x86_64-boost
mingw-w64-ucrt-x86_64-nauty
mingw-w64-ucrt-x86_64-libhomfly
mingw-w64-ucrt-x86_64-symmetrica
mingw-w64-ucrt-x86_64-glpk
mingw-w64-ucrt-x86_64-gc
path-type: inherit

- name: Build
shell: msys2 {0}
run: |
# Install build dependencies manually as workaround for https://github.com/astral-sh/uv/issues/1516
uv pip install \
meson-python \
"cysignals >=1.11.2, != 1.12.0" \
"cython >=3.0, != 3.0.3, < 3.1.0" \
"gmpy2 >=2.1.5" \
memory_allocator \
"numpy >=1.25" \
jinja2 \
setuptools
uv sync --frozen --inexact --no-build-isolation -v --no-editable --config-settings=builddir=builddir

- name: Test
shell: msys2 {0}
run: |
uv run --frozen ./sage -t --all -p4 || true

- name: Upload log
uses: actions/upload-artifact@v4.5.0
if: failure() || success()
with:
name: mingw-log
path: |
build/cp313/meson-logs/
build/cp313/meson-info/
1 change: 1 addition & 0 deletions src/doc/en/installation/source.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ To compile and install Sage in editable install, then just use:
$ uv pip install \
meson-python \
"cypari2 >=2.2.1" \
"cysignals >=1.11.2, != 1.12.0" \
"cython >=3.0, != 3.0.3, != 3.1.0" \
"gmpy2 >=2.1.5" \
memory_allocator \
Expand Down
4 changes: 2 additions & 2 deletions src/sage/config.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ NTL_LIBDIR = "@NTL_LIBDIR@"
# Path to the ecl-config script
ECL_CONFIG = "@SAGE_ECL_CONFIG@".replace("${prefix}", SAGE_LOCAL)

# Path to the nauty binaries; of the form "/path/to/" or "/path/to/nauty-"
SAGE_NAUTY_BINS_PREFIX = "@SAGE_NAUTY_BINS_PREFIX@"
# Path to the nauty binaries; of the form "/path/to" or "/path/to/nauty-"
SAGE_NAUTY_BINS_PREFIX = r"@SAGE_NAUTY_BINS_PREFIX@"

SAGE_ECMBIN = "@SAGE_ECMBIN@"

Expand Down
45 changes: 28 additions & 17 deletions src/sage/features/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ def absolute_filename(self) -> str:

class Executable(FileFeature):
r"""
A feature describing an executable in the ``PATH``.
A feature describing an executable.

In an installation of Sage with ``SAGE_LOCAL`` different from ``SAGE_VENV``, the
executable is searched first in ``SAGE_VENV/bin``, then in ``SAGE_LOCAL/bin``,
Expand All @@ -680,7 +680,9 @@ class Executable(FileFeature):
sage: Executable(name='does-not-exist', executable='does-not-exist-xxxxyxyyxyy').is_present()
FeatureTestResult('does-not-exist', False)
"""
def __init__(self, name, executable, **kwds):
executable: Path

def __init__(self, name: str, executable: Path | str, **kwds) -> None:
r"""
TESTS::

Expand All @@ -689,9 +691,9 @@ def __init__(self, name, executable, **kwds):
True
"""
Feature.__init__(self, name, **kwds)
self.executable = executable
self.executable = Path(executable)

def _is_present(self):
def _is_present(self) -> FeatureTestResult:
r"""
Test whether the executable is on the current PATH and functional.

Expand All @@ -708,7 +710,7 @@ def _is_present(self):
return result
return self.is_functional()

def is_functional(self):
def is_functional(self) -> FeatureTestResult:
r"""
Return whether an executable in the path is functional.

Expand Down Expand Up @@ -744,22 +746,31 @@ def absolute_filename(self) -> str:
sage.features.FeatureNotPresentError: does-not-exist is not available.
Executable 'does-not-exist-xxxxyxyyxyy' not found on PATH.
"""
if SAGE_LOCAL:
if Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve():
# As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV,
# SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV.
search_path = ':'.join([os.path.join(SAGE_VENV, 'bin'),
os.path.join(SAGE_LOCAL, 'bin')])
path = shutil.which(self.executable, path=search_path)
if path is not None:
return path
if self.executable.is_absolute() and self.executable.exists():
return str(self.executable.resolve())

if (
SAGE_LOCAL
and SAGE_VENV
and Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve()
):
# As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV,
# SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV.
search_paths = os.pathsep.join(
[os.path.join(SAGE_VENV, "bin"), os.path.join(SAGE_LOCAL, "bin")]
)
path = shutil.which(self.executable, path=search_paths)
if path is not None:
return path
# Now look up in the regular PATH.
path = shutil.which(self.executable)
if path is not None:
return path
raise FeatureNotPresentError(self,
reason="Executable {executable!r} not found on PATH.".format(executable=self.executable),
resolution=self.resolution())
raise FeatureNotPresentError(
self,
reason=f"Executable '{str(self.executable)}' not found on PATH.",
resolution=self.resolution(),
)


class StaticFile(FileFeature):
Expand Down
10 changes: 9 additions & 1 deletion src/sage/features/nauty.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# https://www.gnu.org/licenses/
# *****************************************************************************

from pathlib import Path

from sage.env import SAGE_NAUTY_BINS_PREFIX

from . import Executable
Expand All @@ -36,10 +38,16 @@ def __init__(self, name):
sage: isinstance(NautyExecutable('geng'), NautyExecutable)
True
"""
if SAGE_NAUTY_BINS_PREFIX is None:
raise ValueError("SAGE_NAUTY_BINS_PREFIX is not set.")
if SAGE_NAUTY_BINS_PREFIX.endswith("-"):
executable = f"{SAGE_NAUTY_BINS_PREFIX}{name}"
else:
executable = Path(SAGE_NAUTY_BINS_PREFIX) / name
Executable.__init__(
self,
name=f"nauty_{name}",
executable=f"{SAGE_NAUTY_BINS_PREFIX}{name}",
executable=executable,
spkg="nauty",
type="standard",
)
Expand Down
13 changes: 8 additions & 5 deletions src/sage/graphs/base/static_dense_graph.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ def connected_full_subgraphs(G, edges_only=False, labels=False,
raise ValueError("the input (di)graph is not connected")

for d in G.degree():
if d >= 8 * sizeof(unsigned long) - 1:
if d >= 8 * sizeof(mp_limb_t) - 1:
raise ValueError("the degree of the graph is too large for this method")

cdef Py_ssize_t n = G.order()
Expand Down Expand Up @@ -640,7 +640,7 @@ def connected_full_subgraphs(G, edges_only=False, labels=False,

cdef MemoryAllocator mem = MemoryAllocator()
cdef int * order = <int *> mem.calloc(n, sizeof(int))
cdef mp_bitcnt_t * n_cpt = <mp_bitcnt_t *> mem.calloc(n, sizeof(mp_bitcnt_t))
cdef mp_limb_t * n_cpt = <mp_limb_t *> mem.calloc(n, sizeof(mp_limb_t))

# We use several bitsets to store the current boundary and active neighbors.
# We also need another bitset that we create at the same time
Expand All @@ -659,10 +659,10 @@ def connected_full_subgraphs(G, edges_only=False, labels=False,
bitset_complement(active, active)
bitset_discard(active, 0)
bitset_copy(neighborhoods.rows[0], DG.rows[0])
n_cpt[0] = 1 << bitset_len(DG.rows[0])
n_cpt[0] = (<mp_limb_t>1) << bitset_len(DG.rows[0])

cdef long u, v, j
cdef mp_bitcnt_t c
cdef mp_limb_t c
cdef Py_ssize_t num_edges = 0
cdef list E = []
cdef list edges
Expand Down Expand Up @@ -734,7 +734,10 @@ def connected_full_subgraphs(G, edges_only=False, labels=False,
# prepare neighborhood of u
bitset_and(neighborhoods.rows[i], active, DG.rows[u])
j = bitset_len(neighborhoods.rows[i])
n_cpt[i] = bool(j) << j # 0 if not j else 2^j
if j:
n_cpt[i] = (<mp_limb_t>1) << j # 2^j possible neighborhoods
else:
n_cpt[i] = 0

sig_on()
binary_matrix_free(boundaries)
Expand Down
7 changes: 6 additions & 1 deletion src/sage/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ foreach prog : nauty_progs
)
endif
if nauty_check.found()
nauty_bins_prefix = nauty_check.full_path().replace(prog, '')
nauty_bins_prefix = nauty_check.full_path().replace(prog + '.EXE', '').replace(
prog,
'',
).strip(
'\\',
)
endif
endforeach
conf_data.set('SAGE_NAUTY_BINS_PREFIX', nauty_bins_prefix)
Expand Down
4 changes: 2 additions & 2 deletions src/sage/plot/plot3d/shapes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ EXAMPLES::
# https://www.gnu.org/licenses/
# ****************************************************************************

from libc.math cimport sqrt, sin, cos, acos, M_PI
from libc.math cimport sqrt, sin, cos, acos
from sage.rings.real_double import RDF
from sage.modules.free_module_element import vector
from sage.misc.decorators import rename_keyword
from sage.plot.plot3d.base import Graphics3dGroup
from sage.plot.plot3d.index_face_set cimport IndexFaceSet, PrimitiveObject
from sage.plot.plot3d.transform cimport point_c

from sage.arith.constants cimport M_PI

# Helper function to check that Box input is right
def validate_frame_size(size):
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ foreach name, pyx : extension_data
elif name == 'real_double_element_gsl'
deps += [gsl]
elif name == 'real_mpfi'
deps += [mpfi]
deps += [mpfi, mpfr]
elif name == 'real_mpfr'
deps += [gmpy2, mpfr]
elif name == 'tate_algebra_element'
Expand Down
3 changes: 2 additions & 1 deletion src/sage/stats/hmm/chmm.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ AUTHOR:
# ***************************************************************************

from cpython.object cimport PyObject_RichCompare
from libc.math cimport log, sqrt, exp, isnormal, isfinite, M_PI
from libc.math cimport log, sqrt, exp, isnormal, isfinite
from sage.arith.constants cimport M_PI
cdef double sqrt2pi = sqrt(2*M_PI)
from cysignals.signals cimport sig_on, sig_off

Expand Down
1 change: 1 addition & 0 deletions subprojects/packagefiles/singular/factory/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ has_iostream_h = cpp.has_header('iostream.h')
conf.set('NOSTREAMIO', not has_iostream_h)#, 'strstream.h', 'fstream.h', 'iostream', 'string', 'fstream', 'ctype.h'))
conf.set('HAVE_IOSTREAM_H', has_iostream_h)
conf.set('FACTORYVERSION', meson.project_version())
conf.set('SIZEOF_LONG', cpp.sizeof('long'))
factory_configuration = '@0@ in @1@'.format(meson.project_version(), meson.project_source_root())
conf.set_quoted('FACTORYCONFIGURATION', factory_configuration)

Expand Down
Loading