From 6f8eb008ae6ce924dcf864c56444b2fc4cd19ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= Date: Tue, 16 Jun 2026 10:20:50 +0200 Subject: [PATCH 1/3] Defer loading of NumPy and SciPy by using local imports --- .../geometry/_core/transformations_numpy.py | 28 ++++++-- src/compas/geometry/bbox_numpy.py | 29 +++++---- src/compas/geometry/bestfit_numpy.py | 13 ++-- src/compas/geometry/hull_numpy.py | 10 +-- src/compas/geometry/icp_numpy.py | 19 +++--- src/compas/geometry/pca_numpy.py | 7 +- src/compas/geometry/trimesh_descent_numpy.py | 4 +- src/compas/geometry/trimesh_gradient_numpy.py | 6 +- src/compas/geometry/trimesh_matrices_numpy.py | 15 +++-- .../geometry/trimesh_pull_points_numpy.py | 14 ++-- .../geometry/trimesh_samplepoints_numpy.py | 21 +++--- .../geometry/trimesh_smoothing_numpy.py | 4 +- src/compas/linalg.py | 65 +++++++++++++------ src/compas/matrices.py | 34 ++++++---- 14 files changed, 167 insertions(+), 102 deletions(-) diff --git a/src/compas/geometry/_core/transformations_numpy.py b/src/compas/geometry/_core/transformations_numpy.py index 7fc1802754ef..b7411478683c 100644 --- a/src/compas/geometry/_core/transformations_numpy.py +++ b/src/compas/geometry/_core/transformations_numpy.py @@ -1,10 +1,3 @@ -from numpy import asarray -from numpy import hstack -from numpy import ones -from numpy import tile -from numpy import vectorize -from scipy.linalg import solve # type: ignore - from ._algebra import cross_vectors @@ -31,6 +24,8 @@ def transform_points_numpy(points, T): >>> points_transformed = transform_points_numpy(points, T) """ + from numpy import asarray + T = asarray(T) points = homogenize_numpy(points, w=1.0) return dehomogenize_numpy(points.dot(T.T)) @@ -59,6 +54,8 @@ def transform_vectors_numpy(vectors, T): >>> vectors_transformed = transform_vectors_numpy(vectors, T) """ + from numpy import asarray + T = asarray(T) vectors = homogenize_numpy(vectors, w=0.0) return dehomogenize_numpy(vectors.dot(T.T)) @@ -87,6 +84,8 @@ def transform_frames_numpy(frames, T): >>> transformed_frames = transform_frames_numpy(frames, T) """ + from numpy import asarray + T = asarray(T) points_and_vectors = homogenize_and_flatten_frames_numpy(frames) return dehomogenize_and_unflatten_frames_numpy(points_and_vectors.dot(T.T)) @@ -117,6 +116,9 @@ def world_to_local_coordinates_numpy(frame, xyz): True """ + from numpy import asarray + from scipy.linalg import solve # type: ignore + origin = frame[0] uvw = [frame[1], frame[2], cross_vectors(frame[1], frame[2])] uvw = asarray(uvw).T @@ -154,6 +156,8 @@ def local_to_world_coordinates_numpy(frame, rst): True """ + from numpy import asarray + origin = frame[0] uvw = [frame[1], frame[2], cross_vectors(frame[1], frame[2])] @@ -191,6 +195,10 @@ def homogenize_numpy(data, w=1.0): True """ + from numpy import asarray + from numpy import hstack + from numpy import ones + data = asarray(data) data = hstack((data, w * ones((data.shape[0], 1)))) return data @@ -216,6 +224,8 @@ def dehomogenize_numpy(data): True """ + from numpy import asarray + from numpy import vectorize def func(a): return a if a else 1.0 @@ -248,6 +258,10 @@ def homogenize_and_flatten_frames_numpy(frames): True """ + from numpy import asarray + from numpy import hstack + from numpy import tile + n = len(frames) frames = asarray(frames).reshape(n * 3, 3) extend = tile(asarray([1, 0, 0]).reshape(3, 1), (n, 1)) diff --git a/src/compas/geometry/bbox_numpy.py b/src/compas/geometry/bbox_numpy.py index c3704b24fe24..5901f093655a 100644 --- a/src/compas/geometry/bbox_numpy.py +++ b/src/compas/geometry/bbox_numpy.py @@ -1,15 +1,3 @@ -from numpy import amax -from numpy import amin -from numpy import argmax -from numpy import argmin -from numpy import array -from numpy import asarray -from numpy import dot -from numpy import sum -from numpy import vstack -from numpy import zeros -from scipy.spatial import ConvexHull - from compas.geometry import length_vector from compas.geometry import local_axes from compas.geometry import local_to_world_coordinates_numpy @@ -78,6 +66,9 @@ def oriented_bounding_box_numpy(points, tol=None): True """ + from numpy import asarray + from numpy import vstack + points = asarray(points) n, dim = points.shape @@ -156,6 +147,9 @@ def oriented_bounding_box_xy_numpy(points): :func:`compas.geometry.oriented_bounding_box_numpy` """ + from numpy import asarray + from numpy import zeros + points = asarray(points) n, dim = points.shape @@ -192,6 +186,10 @@ def minimum_volume_box(points, return_size=False): XYZ coordinates of 8 points defining a box. """ + from numpy import amax + from numpy import amin + from scipy.spatial import ConvexHull + hull = ConvexHull(points) xyz = points[hull.vertices] boxes = [] @@ -248,6 +246,13 @@ def minimum_area_rectangle_xy(points, return_size=False): XYZ coordinates of 4 points defining a rectangle. """ + from numpy import argmax + from numpy import argmin + from numpy import array + from numpy import dot + from numpy import sum + from scipy.spatial import ConvexHull + boxes = [] points = points[:, :2] diff --git a/src/compas/geometry/bestfit_numpy.py b/src/compas/geometry/bestfit_numpy.py index 1317e4ccf4e5..3caed051632d 100644 --- a/src/compas/geometry/bestfit_numpy.py +++ b/src/compas/geometry/bestfit_numpy.py @@ -1,9 +1,3 @@ -from numpy import asarray -from numpy import sqrt -from numpy import zeros -from numpy.linalg import lstsq -from scipy.optimize import leastsq - from compas.geometry import local_to_world_coordinates_numpy from compas.geometry import pca_numpy from compas.geometry import world_to_local_coordinates_numpy @@ -155,6 +149,9 @@ def bestfit_circle_numpy(points): Available at: http://scipy-cookbook.readthedocs.io/items/Least_Squares_Circle.html. """ + from numpy import sqrt + from scipy.optimize import leastsq + o, uvw, _ = pca_numpy(points) frame = [o, uvw[0], uvw[1]] @@ -237,6 +234,10 @@ def bestfit_sphere_numpy(points): Available at: https://jekel.me/2015/Least-Squares-Sphere-Fit/. """ + from numpy import asarray + from numpy import sqrt + from numpy import zeros + from numpy.linalg import lstsq # Assemble the A matrix spX = asarray([p[0] for p in points]) diff --git a/src/compas/geometry/hull_numpy.py b/src/compas/geometry/hull_numpy.py index d438e37646b7..a97768738dc2 100644 --- a/src/compas/geometry/hull_numpy.py +++ b/src/compas/geometry/hull_numpy.py @@ -1,7 +1,3 @@ -from numpy import asarray -from scipy.spatial import ConvexHull - - def convex_hull_numpy(points): """Compute the convex hull of a set of points. @@ -34,6 +30,9 @@ def convex_hull_numpy(points): with :func:`compas.topology.unify_cycles`. """ + from numpy import asarray + from scipy.spatial import ConvexHull + points = asarray(points) n, dim = points.shape @@ -71,6 +70,9 @@ def convex_hull_xy_numpy(points): convex_hull_numpy """ + from numpy import asarray + from scipy.spatial import ConvexHull + points = asarray(points) n, dim = points.shape diff --git a/src/compas/geometry/icp_numpy.py b/src/compas/geometry/icp_numpy.py index 661e656b8d65..1cd5935642a5 100644 --- a/src/compas/geometry/icp_numpy.py +++ b/src/compas/geometry/icp_numpy.py @@ -1,12 +1,3 @@ -import numpy as np -from numpy import argmin -from numpy import asarray -from numpy.linalg import det -from numpy.linalg import multi_dot -from scipy.linalg import norm -from scipy.linalg import svd -from scipy.spatial.distance import cdist - from compas.geometry import pca_numpy from compas.geometry import transform_points_numpy from compas.linalg import normrow @@ -14,6 +5,10 @@ def bestfit_transform(A, B): + import numpy as np + from numpy.linalg import det + from scipy.linalg import svd + n, m = A.shape Am = np.mean(A, axis=0) Bm = np.mean(B, axis=0) @@ -76,6 +71,12 @@ def icp_numpy(source, target, tol=None, maxiter=100): The algorithm terminates when the alignment error is below a specified tolerance. """ + from numpy import argmin + from numpy import asarray + from numpy.linalg import multi_dot + from scipy.linalg import norm + from scipy.spatial.distance import cdist + from compas.geometry import Frame from compas.geometry import Transformation diff --git a/src/compas/geometry/pca_numpy.py b/src/compas/geometry/pca_numpy.py index 9e2fcf13d3f4..351d876a908a 100644 --- a/src/compas/geometry/pca_numpy.py +++ b/src/compas/geometry/pca_numpy.py @@ -1,7 +1,3 @@ -from numpy import asarray -from scipy.linalg import svd # type: ignore - - def pca_numpy(data): """Compute the principle components of a set of data points. @@ -37,6 +33,9 @@ def pca_numpy(data): -------- >>> """ + from numpy import asarray + from scipy.linalg import svd # type: ignore + X = asarray(data) n, dim = X.shape diff --git a/src/compas/geometry/trimesh_descent_numpy.py b/src/compas/geometry/trimesh_descent_numpy.py index 7c219efa44e2..f9492c108bd3 100644 --- a/src/compas/geometry/trimesh_descent_numpy.py +++ b/src/compas/geometry/trimesh_descent_numpy.py @@ -1,5 +1,3 @@ -from numpy import asarray - from .trimesh_gradient_numpy import trimesh_gradient_numpy @@ -17,6 +15,8 @@ def trimesh_descent_numpy(M): The descent directions. """ + from numpy import asarray + vertices, faces = M V = asarray(vertices) F = asarray(faces) diff --git a/src/compas/geometry/trimesh_gradient_numpy.py b/src/compas/geometry/trimesh_gradient_numpy.py index 7987686fa4e4..101bc7cca841 100644 --- a/src/compas/geometry/trimesh_gradient_numpy.py +++ b/src/compas/geometry/trimesh_gradient_numpy.py @@ -1,6 +1,3 @@ -import numpy as np -from scipy.sparse import coo_matrix # type: ignore - from compas.linalg import normalizerow from compas.linalg import normrow from compas.linalg import rot90 @@ -29,6 +26,9 @@ def trimesh_gradient_numpy(M, rtype="array"): and the coordinate difference vectors associated with the edges """ + import numpy as np + from scipy.sparse import coo_matrix # type: ignore + V, F = M v = V.shape[0] f = F.shape[0] diff --git a/src/compas/geometry/trimesh_matrices_numpy.py b/src/compas/geometry/trimesh_matrices_numpy.py index d3a1f2186c54..f9422e2d391d 100644 --- a/src/compas/geometry/trimesh_matrices_numpy.py +++ b/src/compas/geometry/trimesh_matrices_numpy.py @@ -1,10 +1,3 @@ -from numpy import asarray -from numpy import bincount -from numpy import cross -from numpy import zeros -from scipy.sparse import coo_matrix -from scipy.sparse import spdiags - from compas.geometry import cross_vectors from compas.geometry import dot_vectors from compas.geometry import length_vector @@ -107,6 +100,8 @@ def trimesh_cotangent_laplacian_matrix(mesh, rtype="csr"): `Laplacian Mesh Optimization `_. """ + from scipy.sparse import coo_matrix + vertex_index = mesh.vertex_index() n = mesh.number_of_vertices() data = [] @@ -173,6 +168,12 @@ def trimesh_vertexarea_matrix(mesh): [0.1666, 0.1666, 0.1666] """ + from numpy import asarray + from numpy import bincount + from numpy import cross + from numpy import zeros + from scipy.sparse import spdiags + vertex_index = mesh.vertex_index() xyz = asarray(mesh.vertices_attributes("xyz"), dtype=float) tris = asarray( diff --git a/src/compas/geometry/trimesh_pull_points_numpy.py b/src/compas/geometry/trimesh_pull_points_numpy.py index 0b686339bab5..9b51f1f50ff9 100644 --- a/src/compas/geometry/trimesh_pull_points_numpy.py +++ b/src/compas/geometry/trimesh_pull_points_numpy.py @@ -1,7 +1,3 @@ -import numpy as np -from scipy.linalg import solve -from scipy.spatial import distance_matrix - from compas.geometry import cross_vectors from compas.geometry import is_ccw_xy from compas.geometry import is_point_in_triangle @@ -29,6 +25,9 @@ def trimesh_pull_points_numpy(M, points): It will just be treated as if it is... """ + import numpy as np + from scipy.spatial import distance_matrix + vertices, faces = M # preprocess vertices = np.array(vertices, dtype=np.float64).reshape((-1, 3)) @@ -65,6 +64,8 @@ def _is_point_in_edgezone(p, p0, p1): def _compute_point_on_segment(p, p0, p1): + import numpy as np + a = p1[1] - p0[1] b = p0[0] - p1[0] c = p1[0] * p0[1] - p1[1] * p0[0] @@ -75,6 +76,8 @@ def _compute_point_on_segment(p, p0, p1): def _triangle_xform(triangle): + import numpy as np + o = triangle[0] u = triangle[1] - o v = triangle[2] - o @@ -85,6 +88,9 @@ def _triangle_xform(triangle): def _find_closest_component(point, vertices, triangles, closest_tris, closest_vi): + import numpy as np + from scipy.linalg import solve + distance = None projection = None component = None diff --git a/src/compas/geometry/trimesh_samplepoints_numpy.py b/src/compas/geometry/trimesh_samplepoints_numpy.py index 0d1a659be178..22c208c640ae 100644 --- a/src/compas/geometry/trimesh_samplepoints_numpy.py +++ b/src/compas/geometry/trimesh_samplepoints_numpy.py @@ -1,14 +1,3 @@ -from numpy import array -from numpy import clip -from numpy import cross -from numpy import finfo -from numpy import float64 -from numpy import sqrt -from numpy.linalg import norm -from numpy.random import choice -from numpy.random import rand - - def trimesh_samplepoints_numpy(M, num_points=1000, return_normals=False): """Compute sample points on a triangle mesh surface. @@ -51,6 +40,16 @@ def trimesh_samplepoints_numpy(M, num_points=1000, return_normals=False): >>> X, Y, Z = x + pts_normals[:, 0], y + pts_normals[:, 1], z + pts_normals[:, 2] """ + from numpy import array + from numpy import clip + from numpy import cross + from numpy import finfo + from numpy import float64 + from numpy import sqrt + from numpy.linalg import norm + from numpy.random import choice + from numpy.random import rand + # if mesh.is_empty(): # raise ValueError("Mesh is empty.") # if not mesh.is_trimesh(): diff --git a/src/compas/geometry/trimesh_smoothing_numpy.py b/src/compas/geometry/trimesh_smoothing_numpy.py index 702c06660833..4d9f4cc58976 100644 --- a/src/compas/geometry/trimesh_smoothing_numpy.py +++ b/src/compas/geometry/trimesh_smoothing_numpy.py @@ -1,5 +1,3 @@ -from numpy import array - from .trimesh_matrices_numpy import trimesh_cotangent_laplacian_matrix @@ -21,6 +19,8 @@ def trimesh_smooth_laplacian_cotangent(trimesh, fixed, kmax=10): The mesh is modified in place. """ + from numpy import array + for k in range(kmax): V = array(trimesh.vertices_attributes("xyz")) L = trimesh_cotangent_laplacian_matrix(trimesh) diff --git a/src/compas/linalg.py b/src/compas/linalg.py index 3f0f6cd53561..c59c90d8f76b 100644 --- a/src/compas/linalg.py +++ b/src/compas/linalg.py @@ -1,23 +1,6 @@ import sys from functools import wraps -from numpy import absolute -from numpy import array -from numpy import asarray -from numpy import atleast_2d -from numpy import cross -from numpy import nan_to_num -from numpy import nonzero -from numpy import sum -from numpy.linalg import cond -from scipy.linalg import cho_factor # type: ignore -from scipy.linalg import cho_solve # type: ignore -from scipy.linalg import lstsq # type: ignore -from scipy.linalg import qr # type: ignore -from scipy.linalg import svd # type: ignore -from scipy.sparse.linalg import factorized # type: ignore -from scipy.sparse.linalg import spsolve # type: ignore - # ============================================================================== # Fundamentals # ============================================================================== @@ -60,6 +43,10 @@ def nullspace(A, tol=0.001): [ 0.52381648]]) """ + from numpy import asarray + from numpy import atleast_2d + from scipy.linalg import svd # type: ignore + A = atleast_2d(asarray(A, dtype=float)) u, s, vh = svd(A, compute_uv=True) tol = s[0] * tol @@ -98,6 +85,10 @@ def rank(A, tol=0.001): 2 """ + from numpy import asarray + from numpy import atleast_2d + from scipy.linalg import svd # type: ignore + A = atleast_2d(asarray(A, dtype=float)) s = svd(A, compute_uv=False) tol = s[0] * tol @@ -138,6 +129,11 @@ def dof(A, tol=0.001, condition=False): True """ + from numpy import asarray + from numpy import atleast_2d + from numpy.linalg import cond + + A = atleast_2d(asarray(A, dtype=float)) r = rank(A, tol=tol) k = A.shape[1] - r @@ -174,6 +170,11 @@ def pivots(U, tol=None): [0, 1] """ + from numpy import absolute + from numpy import array + from numpy import atleast_2d + from numpy import nonzero + if tol is None: tol = sys.float_info.epsilon U = atleast_2d(array(U, dtype=float)) @@ -212,6 +213,9 @@ def nonpivots(U, tol=None): [2, 3] """ + from numpy import asarray + from numpy import atleast_2d + U = atleast_2d(asarray(U, dtype=float)) cols = pivots(U, tol=tol) return list(set(range(U.shape[1])) - set(cols)) @@ -241,6 +245,10 @@ def rref(A, tol=None): >>> """ + from numpy import asarray + from numpy import atleast_2d + from scipy.linalg import qr # type: ignore + A = atleast_2d(asarray(A, dtype=float)) # do qr with column pivoting @@ -249,7 +257,7 @@ def rref(A, tol=None): # as leading element _, U = qr(A) # type: ignore lead_pos = 0 - num_rows, num_cols = U.shape + num_rows, num_cols = U.shape # type: ignore for r in range(num_rows): if lead_pos >= num_cols: return @@ -341,6 +349,8 @@ def _chofactor(A): [-5., 0., 3.]]), False) """ + from scipy.linalg import cho_factor # type: ignore + return cho_factor(A) @@ -373,6 +383,8 @@ def _lufactorized(A): array([ 1., -2., -2.]) """ + from scipy.sparse.linalg import factorized # type: ignore + return factorized(A) @@ -456,6 +468,10 @@ def normrow(A): [2.23606798]]) """ + from numpy import asarray + from numpy import atleast_2d + from numpy import sum + A = atleast_2d(asarray(A, dtype=float)) return (sum(A**2, axis=1) ** 0.5).reshape((-1, 1)) @@ -502,6 +518,8 @@ def normalizerow(A, do_nan_to_num=True): [ 0. , 0.89442719, -0.4472136 ]]) """ + from numpy import nan_to_num + if do_nan_to_num: return nan_to_num(A / normrow(A)) else: @@ -538,6 +556,8 @@ def rot90(vectors, axes): [ 5.3748385 , -7.5247739 , 4.2998708 ]]) """ + from numpy import cross + return normalizerow(cross(axes, vectors)) * normrow(vectors) @@ -573,6 +593,11 @@ def solve_with_known(A, b, x, known): \mathbf{A} \mathbf{x} = \mathbf{b} """ + from numpy.linalg import cond + from scipy.linalg import cho_factor # type: ignore + from scipy.linalg import cho_solve # type: ignore + from scipy.linalg import lstsq # type: ignore + eps = 1 / sys.float_info.epsilon unknown = list(set(range(x.shape[0])) - set(known)) A11 = A[unknown, :][:, unknown] @@ -583,7 +608,7 @@ def solve_with_known(A, b, x, known): x[unknown] = Y return x Y = lstsq(A11, b) - x[unknown] = Y[0] + x[unknown] = Y[0] # type: ignore return x @@ -626,6 +651,8 @@ def spsolve_with_known(A, b, x, known): True """ + from scipy.sparse.linalg import spsolve # type: ignore + unknown = list(set(range(x.shape[0])) - set(known)) A11 = A[unknown, :][:, unknown] A12 = A[unknown, :][:, known] diff --git a/src/compas/matrices.py b/src/compas/matrices.py index 603e0f4c1fc6..c4031b453fb3 100644 --- a/src/compas/matrices.py +++ b/src/compas/matrices.py @@ -1,13 +1,3 @@ -from numpy import abs -from numpy import array -from numpy import asarray -from numpy import tile -from scipy.sparse import coo_matrix # type: ignore -from scipy.sparse import csr_matrix # type: ignore -from scipy.sparse import diags # type: ignore -from scipy.sparse import vstack as svstack # type: ignore - - def _return_matrix(M, rtype): if rtype == "list": return M.toarray().tolist() @@ -43,6 +33,8 @@ def adjacency_matrix(adjacency, rtype="array"): Constructed adjacency matrix. """ + from scipy.sparse import coo_matrix # type: ignore + a = [(1, i, j) for i in range(len(adjacency)) for j in adjacency[i]] data, rows, cols = zip(*a) A = coo_matrix((data, (rows, cols))).asfptype() @@ -65,6 +57,9 @@ def face_matrix(face_vertices, rtype="array", normalize=False): Constructed face matrix. """ + from numpy import array + from scipy.sparse import coo_matrix # type: ignore + if normalize: f = array([(i, j, 1.0 / len(vertices)) for i, vertices in enumerate(face_vertices) for j in vertices]) else: @@ -94,6 +89,8 @@ def degree_matrix(adjacency, rtype="array"): Constructed degree matrix. """ + from scipy.sparse import coo_matrix # type: ignore + d = [(len(adjacency[i]), i, i) for i in range(len(adjacency))] data, rows, cols = zip(*d) D = coo_matrix((data, (rows, cols))).asfptype() @@ -146,6 +143,9 @@ def connectivity_matrix(edges, rtype="array"): [-1., 0., 0., 1.]]) """ + from numpy import array + from scipy.sparse import coo_matrix # type: ignore + m = len(edges) data = array([-1] * m + [1] * m) rows = array(list(range(m)) + list(range(m))) @@ -195,6 +195,8 @@ def laplacian_matrix(edges, normalize=False, rtype="array"): [-1., 0., 0., 1.]]) """ + from scipy.sparse import csr_matrix # type: ignore + C = connectivity_matrix(edges, rtype="csr") L = C.transpose().dot(C) # type: ignore if normalize: @@ -257,12 +259,17 @@ def equilibrium_matrix(C, xyz, free, rtype="array"): [-1., 1., 1.]]) """ + from numpy import asarray + from scipy.sparse import csr_matrix # type: ignore + from scipy.sparse import diags # type: ignore + from scipy.sparse import vstack as svstack # type: ignore + xyz = asarray(xyz, dtype=float) C = csr_matrix(C) xy = xyz[:, :2] uv = C.dot(xy) - U = diags([uv[:, 0].flatten()], [0]) - V = diags([uv[:, 1].flatten()], [0]) + U = diags([uv[:, 0].flatten()], [0]) # type: ignore + V = diags([uv[:, 1].flatten()], [0]) # type: ignore Ct = C.transpose() Cti = Ct[free, :] E = svstack((Cti.dot(U), Cti.dot(V))) @@ -304,6 +311,9 @@ def mass_matrix(Ct, ks, q=0, c=1, tiled=True): (\mathbf{E} \circ \mathbf{A} \oslash \mathbf{l} + \mathbf{f} \oslash \mathbf{l}) """ + from numpy import abs + from numpy import tile + m = c * abs(Ct).dot(ks + q) if tiled: return tile(m.reshape((-1, 1)), (1, 3)) From 785b1f68f61f20fe67a0b5245634df578ff3fd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= Date: Tue, 16 Jun 2026 10:28:02 +0200 Subject: [PATCH 2/3] Improve performance of NumPy detection in data encoders --- src/compas/data/encoders.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/compas/data/encoders.py b/src/compas/data/encoders.py index c77bcdca26ce..ca862fd8a911 100644 --- a/src/compas/data/encoders.py +++ b/src/compas/data/encoders.py @@ -14,7 +14,7 @@ from .exceptions import DecoderError IDictionary = None -numpy_support = False +numpy_support = None # type: bool | None dotnet_support = False # We don't do this from `compas.IPY` to avoid circular imports @@ -27,18 +27,15 @@ except: # noqa: E722 pass -try: - import numpy as np - - try: - np_float = np.float_ - except AttributeError: - np_float = np.float64 - - numpy_support = True -except (ImportError, SyntaxError): - numpy_support = False - +def _is_numpy_available(): # type (...) -> bool + global numpy_support + if numpy_support is None: + try: + import numpy.version # noqa: F401 + numpy_support = True + except (ImportError, SyntaxError): + numpy_support = False + return numpy_support def cls_from_dtype(dtype, inheritance=None): # type: (...) -> Type[Data] """Get the class object corresponding to a COMPAS data type specification. @@ -144,7 +141,8 @@ def default(self, o): if hasattr(o, "__next__"): return list(o) - if numpy_support: + if _is_numpy_available(): + import numpy as np if isinstance(o, np.ndarray): return o.tolist() if isinstance( @@ -164,7 +162,9 @@ def default(self, o): ), # type: ignore ): return int(o) - if isinstance(o, (np_float, np.float16, np.float32, np.float64)): # type: ignore + if hasattr(np, "float_") and isinstance(o, np.float_): # type: ignore + return float(o) + if isinstance(o, (np.float16, np.float32, np.float64)): # type: ignore return float(o) if isinstance(o, np.bool_): return bool(o) From 2f5932258fd62cfae482104e77a0df2faa5063fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Miguel=20Moreno?= Date: Tue, 16 Jun 2026 10:29:13 +0200 Subject: [PATCH 3/3] Apply corrections from linter --- src/compas_rhino/geometry/brep/trim.py | 1 - src/compas_rhino/geometry/curves/curve.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compas_rhino/geometry/brep/trim.py b/src/compas_rhino/geometry/brep/trim.py index e15f3e0e182f..0612513286ed 100644 --- a/src/compas_rhino/geometry/brep/trim.py +++ b/src/compas_rhino/geometry/brep/trim.py @@ -8,7 +8,6 @@ from compas_rhino.geometry import RhinoNurbsCurve from .edge import RhinoBrepEdge - from .vertex import RhinoBrepVertex diff --git a/src/compas_rhino/geometry/curves/curve.py b/src/compas_rhino/geometry/curves/curve.py index dca2c2bb90da..6fddc4f41d33 100644 --- a/src/compas_rhino/geometry/curves/curve.py +++ b/src/compas_rhino/geometry/curves/curve.py @@ -6,6 +6,7 @@ from compas.geometry import Curve from compas.geometry import Plane +from compas_rhino.conversions import ConversionError from compas_rhino.conversions import box_to_compas from compas_rhino.conversions import plane_to_compas_frame from compas_rhino.conversions import plane_to_rhino @@ -14,7 +15,6 @@ from compas_rhino.conversions import polyline_to_compas from compas_rhino.conversions import transformation_to_rhino from compas_rhino.conversions import vector_to_compas -from compas_rhino.conversions import ConversionError class RhinoCurve(Curve):