Skip to content

Commit

Permalink
gh-39316: use memory allocator in sage/combinat/designs/
Browse files Browse the repository at this point in the history
    
Use MemoryAllocator in `src/sage/combinat/desings/designs_pyx.pyx` and
`src/sage/combinat/designs/evenly_distributed_sets.pyx` to simplify
parts of the code.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - #12345: short description why this is a dependency -->
<!-- - #34567: ... -->
    
URL: #39316
Reported by: David Coudert
Reviewer(s): Kwankyu Lee
  • Loading branch information
Release Manager committed Jan 16, 2025
2 parents d37ff1a + 5b5e7bc commit 3503f4c
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 65 deletions.
54 changes: 11 additions & 43 deletions src/sage/combinat/designs/designs_pyx.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ from sage.data_structures.bitset_base cimport *

from libc.string cimport memset

from cysignals.memory cimport sig_malloc, sig_calloc, sig_realloc, sig_free
from cysignals.memory cimport sig_malloc, sig_realloc, sig_free
from memory_allocator cimport MemoryAllocator

from sage.misc.unknown import Unknown

Expand Down Expand Up @@ -305,7 +306,8 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O
cdef int i,j,l

# A copy of OA
cdef unsigned short * OAc = <unsigned short *> sig_malloc(k*n2*sizeof(unsigned short))
cdef MemoryAllocator mem = MemoryAllocator()
cdef unsigned short * OAc = <unsigned short *> mem.malloc(k*n2*sizeof(unsigned short))

cdef unsigned short * C1
cdef unsigned short * C2
Expand All @@ -321,7 +323,6 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O
if verbose:
print({"OA": "{} is not in the interval [0..{}]".format(x,n-1),
"MOLS": "Entry {} was expected to be in the interval [0..{}]".format(x,n-1)}[terminology])
sig_free(OAc)
return False
OAc[j*n2+i] = x

Expand All @@ -338,14 +339,12 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology='O
bitset_add(seen,n*C1[l]+C2[l])

if bitset_len(seen) != n2: # Have we seen all pairs ?
sig_free(OAc)
bitset_free(seen)
if verbose:
print({"OA": "Columns {} and {} are not orthogonal".format(i,j),
"MOLS": "Squares {} and {} are not orthogonal".format(i,j)}[terminology])
return False

sig_free(OAc)
bitset_free(seen)
return True

Expand Down Expand Up @@ -469,9 +468,8 @@ def is_group_divisible_design(groups, blocks, v, G=None, K=None, lambd=1, verbos
print("{} does not belong to [0,...,{}]".format(x, n-1))
return False

cdef unsigned short * matrix = <unsigned short *> sig_calloc(n*n, sizeof(unsigned short))
if matrix is NULL:
raise MemoryError
cdef MemoryAllocator mem = MemoryAllocator()
cdef unsigned short * matrix = <unsigned short *> mem.calloc(n*n, sizeof(unsigned short))

# Counts the number of occurrences of each pair of points
for b in blocks:
Expand Down Expand Up @@ -500,7 +498,6 @@ def is_group_divisible_design(groups, blocks, v, G=None, K=None, lambd=1, verbos
if not len(g) in G:
if verbose:
print("a group has size {} while G={}".format(len(g),list(G)))
sig_free(matrix)
return False

# Checks that two points of the same group were never covered
Expand All @@ -513,7 +510,6 @@ def is_group_divisible_design(groups, blocks, v, G=None, K=None, lambd=1, verbos
if matrix[ii*n+jj] != 0:
if verbose:
print("the pair ({},{}) belongs to a group but appears in some block".format(ii, jj))
sig_free(matrix)
return False

# We fill the entries with what is expected by the next loop
Expand All @@ -526,11 +522,8 @@ def is_group_divisible_design(groups, blocks, v, G=None, K=None, lambd=1, verbos
if matrix[i*n+j] != l:
if verbose:
print("the pair ({},{}) has been seen {} times but lambda={}".format(i,j,matrix[i*n+j],l))
sig_free(matrix)
return False

sig_free(matrix)

return True if not guess_groups else (True, groups)


Expand Down Expand Up @@ -836,16 +829,11 @@ def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=Fa
cdef dict group_to_int = {v:i for i,v in enumerate(int_to_group)}

# Allocations
cdef int ** x_minus_y = <int **> sig_malloc((n+1)*sizeof(int *))
cdef int * x_minus_y_data = <int *> sig_malloc((n+1)*(n+1)*sizeof(int))
cdef int * M_c = <int *> sig_malloc(k*M_nrows*sizeof(int))
cdef int * G_seen = <int *> sig_malloc((n+1)*sizeof(int))
if (x_minus_y == NULL or x_minus_y_data == NULL or M_c == NULL or G_seen == NULL):
sig_free(x_minus_y)
sig_free(x_minus_y_data)
sig_free(G_seen)
sig_free(M_c)
raise MemoryError
cdef MemoryAllocator mem = MemoryAllocator()
cdef int ** x_minus_y = <int **> mem.malloc((n+1)*sizeof(int *))
cdef int * x_minus_y_data = <int *> mem.malloc((n+1)*(n+1)*sizeof(int))
cdef int * M_c = <int *> mem.malloc(k*M_nrows*sizeof(int))
cdef int * G_seen = <int *> mem.malloc((n+1)*sizeof(int))

# The "x-y" table. If g_i, g_j \in G, then x_minus_y[i][j] is equal to
# group_to_int[g_i-g_j].
Expand Down Expand Up @@ -883,10 +871,6 @@ def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=Fa
if bit:
if verbose:
print("Row {} contains more than one empty entry".format(i))
sig_free(x_minus_y_data)
sig_free(x_minus_y)
sig_free(G_seen)
sig_free(M_c)
return False
bit = True

Expand All @@ -900,10 +884,6 @@ def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=Fa
if verbose:
print("Column {} contains {} empty entries instead of the expected "
"lambda.u={}.{}={}".format(j, ii, lmbda, u, lmbda*u))
sig_free(x_minus_y_data)
sig_free(x_minus_y)
sig_free(G_seen)
sig_free(M_c)
return False

# We are now ready to test every pair of columns
Expand All @@ -917,10 +897,6 @@ def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=Fa
if verbose:
print("Columns {} and {} generate 0 exactly {} times "
"instead of the expected mu(={})".format(i,j,G_seen[0],mu))
sig_free(x_minus_y_data)
sig_free(x_minus_y)
sig_free(G_seen)
sig_free(M_c)
return False

for ii in range(1,n): # bad number of g_ii\in G
Expand All @@ -929,16 +905,8 @@ def is_quasi_difference_matrix(M, G, int k, int lmbda, int mu, int u, verbose=Fa
print("Columns {} and {} do not generate all elements of G "
"exactly lambda(={}) times. The element {} appeared {} "
"times as a difference.".format(i,j,lmbda,int_to_group[ii],G_seen[ii]))
sig_free(x_minus_y_data)
sig_free(x_minus_y)
sig_free(G_seen)
sig_free(M_c)
return False

sig_free(x_minus_y_data)
sig_free(x_minus_y)
sig_free(G_seen)
sig_free(M_c)
return True


Expand Down
37 changes: 15 additions & 22 deletions src/sage/combinat/designs/evenly_distributed_sets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ Classes and methods

cimport cython

from sage.categories.fields import Fields
from libc.limits cimport UINT_MAX
from libc.string cimport memset, memcpy

from cysignals.memory cimport check_malloc, check_calloc, sig_free
from memory_allocator cimport MemoryAllocator

from sage.rings.integer cimport smallInteger

from sage.categories.fields import Fields


cdef class EvenlyDistributedSetsBacktracker:
r"""
Set of evenly distributed subsets in finite fields.
Expand Down Expand Up @@ -168,17 +169,8 @@ cdef class EvenlyDistributedSetsBacktracker:
cdef unsigned int * cosets # e array: cosets of differences of elts in B
cdef unsigned int * t # e array: temporary variable for updates

def __dealloc__(self):
if self.diff != NULL:
sig_free(self.diff[0])
sig_free(self.diff)
if self.ratio != NULL:
sig_free(self.ratio[0])
sig_free(self.ratio)
sig_free(self.min_orb)
sig_free(self.B)
sig_free(self.cosets)
sig_free(self.t)
# MANAGEMENT OF MEMORY
cdef MemoryAllocator mem

def __init__(self, K, k, up_to_isomorphism=True, check=False):
r"""
Expand Down Expand Up @@ -228,20 +220,21 @@ cdef class EvenlyDistributedSetsBacktracker:
self.m = (q - 1) // e
self.K = K

self.diff = <unsigned int **> check_calloc(q, sizeof(unsigned int *))
self.diff[0] = <unsigned int *> check_malloc(q*q*sizeof(unsigned int))
self.mem = MemoryAllocator()
self.diff = <unsigned int **> self.mem.calloc(q, sizeof(unsigned int *))
self.diff[0] = <unsigned int *> self.mem.malloc(q*q*sizeof(unsigned int))
for i in range(1, self.q):
self.diff[i] = self.diff[i-1] + q

self.ratio = <unsigned int **> check_calloc(q, sizeof(unsigned int *))
self.ratio[0] = <unsigned int *> check_malloc(q*q*sizeof(unsigned int))
self.ratio = <unsigned int **> self.mem.calloc(q, sizeof(unsigned int *))
self.ratio[0] = <unsigned int *> self.mem.malloc(q*q*sizeof(unsigned int))
for i in range(1, self.q):
self.ratio[i] = self.ratio[i-1] + q

self.B = <unsigned int *> check_malloc(k*sizeof(unsigned int))
self.min_orb = <unsigned int *> check_malloc(q*sizeof(unsigned int))
self.cosets = <unsigned int *> check_malloc(e*sizeof(unsigned int))
self.t = <unsigned int *> check_malloc(e*sizeof(unsigned int))
self.B = <unsigned int *> self.mem.malloc(k*sizeof(unsigned int))
self.min_orb = <unsigned int *> self.mem.malloc(q*sizeof(unsigned int))
self.cosets = <unsigned int *> self.mem.malloc(e*sizeof(unsigned int))
self.t = <unsigned int *> self.mem.malloc(e*sizeof(unsigned int))

x = K.multiplicative_generator()
list_K = []
Expand Down

0 comments on commit 3503f4c

Please sign in to comment.