Skip to content

Commit 05c4af1

Browse files
committed
Add Singular.jl extension and tests
- Implement Singular-backed methods for HomalgMatrix, syzygies (rows/columns and relative), bases, zero-decision, and left/right division (2- and 3-arg). - Add singular-ext-test.jl and include in test runner.
1 parent 76f3f11 commit 05c4af1

4 files changed

Lines changed: 598 additions & 1 deletion

File tree

Project.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@ version = "0.1.6"
66
[deps]
77
Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a"
88

9+
[weakdeps]
10+
Singular = "bcd08a7b-43d2-5ff7-b6d4-c458787f915c"
11+
12+
[extensions]
13+
MatricesForHomalgSingularExt = "Singular"
14+
915
[compat]
1016
Nemo = "0.48.0, 0.49, 0.50, 0.51, 0.52, 0.53"
17+
Singular = "0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28"
1118
julia = "1.9, 1.10, 1.11"
1219

1320
[extras]
1421
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
22+
Singular = "bcd08a7b-43d2-5ff7-b6d4-c458787f915c"
1523
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1624

1725
[targets]
18-
test = ["Test", "Documenter"]
26+
test = ["Test", "Documenter", "Singular"]
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
module MatricesForHomalgSingularExt
2+
3+
using MatricesForHomalg
4+
import Singular
5+
6+
"""
7+
HomalgMatrix(entries, r, c, R)
8+
Construct a Singular matrix of size r x c over the polynomial ring R,
9+
with entries given by the nested array `entries` (a vector of vectors).
10+
The entries are converted to elements of R using the constructor R(...).
11+
12+
# Examples
13+
```jldoctest
14+
julia> using Singular, MatricesForHomalg
15+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
16+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
17+
julia> entries = [[x, y], [x^2, y^2]]
18+
2-element Vector{Vector{Singular.n_Q}}:
19+
[x, y]
20+
[x^2, y^2]
21+
julia> A = HomalgMatrix(entries, 2, 2, R)
22+
[ x y
23+
x^2 y^2 ]
24+
```
25+
"""
26+
function MatricesForHomalg.HomalgMatrix(entries, nr_rows, nr_cols, R::Singular.PolyRing)
27+
m = MatricesForHomalg.zero_matrix(R, nr_rows, nr_cols)
28+
flat = vcat(entries...)
29+
for i in 1:nr_rows, j in 1:nr_cols
30+
m[i, j] = R(flat[(i - 1) * nr_cols + j])
31+
end
32+
return m
33+
end
34+
35+
"""
36+
SyzygiesOfRows(A::Singular.smatrix)
37+
38+
Compute the matrix of row syzygies of A using Singular.
39+
Returns a matrix whose rows span the left kernel of A,
40+
i.e. all rows X satisfying X * A = 0.
41+
42+
# Examples
43+
```jldoctest
44+
julia> using Singular, MatricesForHomalg
45+
46+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
47+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
48+
49+
julia> A = Singular.Matrix(R, [x y; x^2 y^2; x*y x*y])
50+
x*y
51+
x^2*y^2
52+
x*y*x*y
53+
54+
julia> S = SyzygiesOfRows(A)
55+
x*(-y+x)*1
56+
(-1)*(y+x)*x
57+
58+
julia> iszero(S * A)
59+
true
60+
```
61+
"""
62+
function MatricesForHomalg.SyzygiesOfRows(A::Singular.smatrix)
63+
R = Singular.base_ring(A)
64+
# Columns of transpose(A) are the rows of A.
65+
# Module from those columns, syz finds relations among them,
66+
# i.e. vectors (a1,...,am) with a1*row1 + ... + am*rowm = 0.
67+
M = Singular.Module(Singular.transpose(A))
68+
S = Singular.syz(M)
69+
# Matrix(S) has syzygy vectors as columns; transpose to get rows.
70+
return Singular.transpose(Singular.Matrix(S))
71+
end
72+
73+
"""
74+
SyzygiesOfColumns(A::Singular.smatrix)
75+
76+
Compute the matrix of column syzygies of A using Singular.
77+
Returns a matrix whose columns span the right kernel of A,
78+
i.e. all columns X satisfying A * X = 0.
79+
80+
# Examples
81+
```jldoctest
82+
julia> using Singular, MatricesForHomalg
83+
84+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
85+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
86+
87+
julia> A = Singular.Matrix(R, [x x^2 x*y; y y^2 x*y])
88+
x, x^2, x*y
89+
y, y^2, x*y
90+
91+
julia> S = SyzygiesOfColumns(A)
92+
...
93+
94+
julia> iszero(A * S)
95+
true
96+
```
97+
"""
98+
function MatricesForHomalg.SyzygiesOfColumns(A::Singular.smatrix)
99+
# Columns of A are generators of a submodule of R^m.
100+
# syz finds vectors (a1,...,an) with a1*col1 + ... + an*coln = 0,
101+
# which is exactly A * [a1,...,an]^T = 0.
102+
M = Singular.Module(A)
103+
S = Singular.syz(M)
104+
if Singular.iszero(S)
105+
# syz returns zero module if there are no syzygies, but we want a zero matrix.
106+
R = Singular.base_ring(A)
107+
return Singular.zero_matrix(R, Singular.ncols(A), 0)
108+
else
109+
return Singular.Matrix(S)
110+
end
111+
end
112+
113+
"""
114+
SyzygiesOfRows(A::Singular.smatrix, N::Singular.smatrix)
115+
116+
Compute the matrix of relative row syzygies of A modulo N using Singular.
117+
Returns a matrix whose rows K satisfy K * A + L * N = 0 for some L.
118+
119+
# Examples
120+
```jldoctest
121+
julia> using Singular, MatricesForHomalg
122+
123+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
124+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
125+
126+
julia> v1 = Singular.vector(R, x, y)
127+
x*gen(1)+y*gen(2)
128+
129+
julia> v2 = Singular.vector(R, y, x)
130+
y*gen(1)+x*gen(2)
131+
132+
julia> A = Singular.Matrix(Singular.Module(R, v1, v2))
133+
x*y
134+
y*x
135+
136+
julia> N = Singular.Matrix(Singular.Module(R, v1))
137+
x
138+
y
139+
140+
julia> K = SyzygiesOfRows(A, N)
141+
...
142+
143+
julia> # verify K * A is in the row span of N
144+
```
145+
"""
146+
function MatricesForHomalg.SyzygiesOfRows(A::Singular.smatrix, N::Singular.smatrix)
147+
At = Singular.transpose(A)
148+
Nt = Singular.transpose(N)
149+
St = MatricesForHomalg.SyzygiesOfColumns(At, Nt)
150+
return Singular.transpose(St)
151+
end
152+
153+
"""
154+
SyzygiesOfColumns(A::Singular.smatrix, N::Singular.smatrix)
155+
156+
Compute the matrix of relative column syzygies of A modulo N using Singular.
157+
Returns a matrix whose columns K satisfy A * K + N * L = 0 for some L.
158+
159+
# Examples
160+
```jldoctest
161+
julia> using Singular, MatricesForHomalg
162+
163+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
164+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
165+
166+
julia> v1 = Singular.vector(R, x, y)
167+
x*gen(1)+y*gen(2)
168+
169+
julia> v2 = Singular.vector(R, y, x)
170+
y*gen(1)+x*gen(2)
171+
172+
julia> A = Singular.Matrix(Singular.Module(R, v1, v2))
173+
x*y
174+
y*x
175+
176+
julia> N = Singular.Matrix(Singular.Module(R, v1))
177+
x
178+
y
179+
180+
julia> K = SyzygiesOfColumns(A, N)
181+
...
182+
183+
julia> # verify A * K is in the column span of N
184+
```
185+
"""
186+
function MatricesForHomalg.SyzygiesOfColumns(A::Singular.smatrix, N::Singular.smatrix)
187+
# modulo(M_A, M_N) computes the kernel of R^n -> M_A / (M_A ∩ M_N),
188+
# i.e. vectors (a1,...,an) such that a1*col1(A) + ... + an*coln(A) ∈ Im(N).
189+
M_A = Singular.Module(A)
190+
M_N = Singular.Module(N)
191+
S = Singular.modulo(M_A, M_N)
192+
if Singular.iszero(S)
193+
R = Singular.base_ring(A)
194+
return Singular.zero_matrix(R, Singular.ncols(A), 0)
195+
else
196+
return Singular.Matrix(S)
197+
end
198+
end
199+
200+
## BasisOfRows / BasisOfColumns
201+
202+
function MatricesForHomalg.BasisOfColumns(A::Singular.smatrix)
203+
M = Singular.Module(A)
204+
G = Singular.std(M)
205+
return Singular.Matrix(G)
206+
end
207+
208+
function MatricesForHomalg.BasisOfRows(A::Singular.smatrix)
209+
return Singular.transpose(MatricesForHomalg.BasisOfColumns(Singular.transpose(A)))
210+
end
211+
212+
## DecideZeroRows / DecideZeroColumns
213+
214+
function MatricesForHomalg.DecideZeroColumns(B::Singular.smatrix, A::Singular.smatrix)
215+
M_A = Singular.Module(A)
216+
G = Singular.std(M_A)
217+
M_B = Singular.Module(B)
218+
R = Singular.reduce(M_B, G)
219+
return Singular.Matrix(R)
220+
end
221+
222+
function MatricesForHomalg.DecideZeroRows(B::Singular.smatrix, A::Singular.smatrix)
223+
return Singular.transpose(MatricesForHomalg.DecideZeroColumns(Singular.transpose(B), Singular.transpose(A)))
224+
end
225+
226+
## LeftDivide / RightDivide (two-argument: solve AX = B)
227+
228+
function MatricesForHomalg.SafeLeftDivide(A::Singular.smatrix, B::Singular.smatrix)
229+
M_A = Singular.Module(A)
230+
M_B = Singular.Module(B)
231+
T, rest = Singular.lift(M_A, M_B)
232+
if !Singular.iszero(rest)
233+
error("Unable to solve linear system")
234+
end
235+
return Singular.Matrix(T)
236+
end
237+
238+
function MatricesForHomalg.LeftDivide(A::Singular.smatrix, B::Singular.smatrix)
239+
try
240+
return MatricesForHomalg.SafeLeftDivide(A, B)
241+
catch
242+
return "fail"
243+
end
244+
end
245+
246+
function MatricesForHomalg.SafeRightDivide(B::Singular.smatrix, A::Singular.smatrix)
247+
return Singular.transpose(MatricesForHomalg.SafeLeftDivide(Singular.transpose(A), Singular.transpose(B)))
248+
end
249+
250+
function MatricesForHomalg.RightDivide(B::Singular.smatrix, A::Singular.smatrix)
251+
try
252+
return MatricesForHomalg.SafeRightDivide(B, A)
253+
catch
254+
return "fail"
255+
end
256+
end
257+
258+
## LeftDivide / RightDivide (three-argument: solve AX + LY = B)
259+
260+
function MatricesForHomalg.SafeLeftDivide(A::Singular.smatrix, B::Singular.smatrix, L::Singular.smatrix)
261+
R = Singular.base_ring(A)
262+
nr_rows = Singular.nrows(A)
263+
nr_cols_a = Singular.ncols(A)
264+
nr_cols_l = Singular.ncols(L)
265+
# Combine columns of A and L into one matrix
266+
AL = Singular.zero_matrix(R, nr_rows, nr_cols_a + nr_cols_l)
267+
for i in 1:nr_rows, j in 1:nr_cols_a
268+
AL[i, j] = A[i, j]
269+
end
270+
for i in 1:nr_rows, j in 1:nr_cols_l
271+
AL[i, nr_cols_a + j] = L[i, j]
272+
end
273+
M_AL = Singular.Module(AL)
274+
M_B = Singular.Module(B)
275+
T, rest = Singular.lift(M_AL, M_B)
276+
if !Singular.iszero(rest)
277+
error("Unable to solve linear system")
278+
end
279+
T_mat = Singular.Matrix(T)
280+
# Extract the first nr_cols_a rows (corresponding to A)
281+
result = Singular.zero_matrix(R, nr_cols_a, Singular.ncols(T_mat))
282+
for i in 1:nr_cols_a, j in 1:Singular.ncols(T_mat)
283+
result[i, j] = T_mat[i, j]
284+
end
285+
return result
286+
end
287+
288+
function MatricesForHomalg.LeftDivide(A::Singular.smatrix, B::Singular.smatrix, L::Singular.smatrix)
289+
try
290+
return MatricesForHomalg.SafeLeftDivide(A, B, L)
291+
catch
292+
return "fail"
293+
end
294+
end
295+
296+
function MatricesForHomalg.SafeRightDivide(B::Singular.smatrix, A::Singular.smatrix, L::Singular.smatrix)
297+
return Singular.transpose(MatricesForHomalg.SafeLeftDivide(Singular.transpose(A), Singular.transpose(B), Singular.transpose(L)))
298+
end
299+
300+
function MatricesForHomalg.RightDivide(B::Singular.smatrix, A::Singular.smatrix, L::Singular.smatrix)
301+
try
302+
return MatricesForHomalg.SafeRightDivide(B, A, L)
303+
catch
304+
return "fail"
305+
end
306+
end
307+
308+
end # module

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ include("properties.jl")
99
include("attributes.jl")
1010
include("operations.jl")
1111
include("testmanual.jl")
12+
include("singular-ext-test.jl")

0 commit comments

Comments
 (0)