Skip to content

Commit cf2b422

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 e87f9ab commit cf2b422

4 files changed

Lines changed: 609 additions & 2 deletions

File tree

Project.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
name = "MatricesForHomalg"
22
uuid = "29b9b1b6-efa6-450e-8188-a5a2c25df071"
33
authors = ["Mohamed Barakat <mohamed.barakat@uni-siegen.de>", "Johanna Knecht <johanna.knecht@student.uni-siegen.de>"]
4-
version = "0.1.6"
4+
version = "0.1.7"
55

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: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
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+
if Singular.iszero(S)
71+
# syz returns zero module if there are no syzygies, but we want a zero matrix.
72+
return Singular.zero_matrix(R, 0, Singular.nrows(A))
73+
else
74+
return Singular.transpose(Singular.Matrix(S))
75+
end
76+
end
77+
78+
"""
79+
SyzygiesOfColumns(A::Singular.smatrix)
80+
81+
Compute the matrix of column syzygies of A using Singular.
82+
Returns a matrix whose columns span the right kernel of A,
83+
i.e. all columns X satisfying A * X = 0.
84+
85+
# Examples
86+
```jldoctest
87+
julia> using Singular, MatricesForHomalg
88+
89+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
90+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
91+
92+
julia> A = Singular.Matrix(R, [x x^2 x*y; y y^2 x*y])
93+
x, x^2, x*y
94+
y, y^2, x*y
95+
96+
julia> S = SyzygiesOfColumns(A)
97+
...
98+
99+
julia> iszero(A * S)
100+
true
101+
```
102+
"""
103+
function MatricesForHomalg.SyzygiesOfColumns(A::Singular.smatrix)
104+
# Columns of A are generators of a submodule of R^m.
105+
# syz finds vectors (a1,...,an) with a1*col1 + ... + an*coln = 0,
106+
# which is exactly A * [a1,...,an]^T = 0.
107+
M = Singular.Module(A)
108+
S = Singular.syz(M)
109+
if Singular.iszero(S)
110+
# syz returns zero module if there are no syzygies, but we want a zero matrix.
111+
R = Singular.base_ring(A)
112+
return Singular.zero_matrix(R, Singular.ncols(A), 0)
113+
else
114+
return Singular.Matrix(S)
115+
end
116+
end
117+
118+
"""
119+
SyzygiesOfRows(A::Singular.smatrix, N::Singular.smatrix)
120+
121+
Compute the matrix of relative row syzygies of A modulo N using Singular.
122+
Returns a matrix whose rows K satisfy K * A + L * N = 0 for some L.
123+
124+
# Examples
125+
```jldoctest
126+
julia> using Singular, MatricesForHomalg
127+
128+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
129+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
130+
131+
julia> v1 = Singular.vector(R, x, y)
132+
x*gen(1)+y*gen(2)
133+
134+
julia> v2 = Singular.vector(R, y, x)
135+
y*gen(1)+x*gen(2)
136+
137+
julia> A = Singular.Matrix(Singular.Module(R, v1, v2))
138+
x*y
139+
y*x
140+
141+
julia> N = Singular.Matrix(Singular.Module(R, v1))
142+
x
143+
y
144+
145+
julia> K = SyzygiesOfRows(A, N)
146+
...
147+
148+
julia> # verify K * A is in the row span of N
149+
```
150+
"""
151+
function MatricesForHomalg.SyzygiesOfRows(A::Singular.smatrix, N::Singular.smatrix)
152+
At = Singular.transpose(A)
153+
Nt = Singular.transpose(N)
154+
St = MatricesForHomalg.SyzygiesOfColumns(At, Nt)
155+
return Singular.transpose(St)
156+
end
157+
158+
"""
159+
SyzygiesOfColumns(A::Singular.smatrix, N::Singular.smatrix)
160+
161+
Compute the matrix of relative column syzygies of A modulo N using Singular.
162+
Returns a matrix whose columns K satisfy A * K + N * L = 0 for some L.
163+
164+
# Examples
165+
```jldoctest
166+
julia> using Singular, MatricesForHomalg
167+
168+
julia> R, (x, y) = Singular.polynomial_ring(Singular.QQ, ["x", "y"])
169+
(Singular polynomial ring (QQ),(x,y),(dp(2),C), spoly{Singular.n_Q}[x, y])
170+
171+
julia> v1 = Singular.vector(R, x, y)
172+
x*gen(1)+y*gen(2)
173+
174+
julia> v2 = Singular.vector(R, y, x)
175+
y*gen(1)+x*gen(2)
176+
177+
julia> A = Singular.Matrix(Singular.Module(R, v1, v2))
178+
x*y
179+
y*x
180+
181+
julia> N = Singular.Matrix(Singular.Module(R, v1))
182+
x
183+
y
184+
185+
julia> K = SyzygiesOfColumns(A, N)
186+
...
187+
188+
julia> # verify A * K is in the column span of N
189+
```
190+
"""
191+
function MatricesForHomalg.SyzygiesOfColumns(A::Singular.smatrix, N::Singular.smatrix)
192+
# modulo(M_A, M_N) computes the kernel of R^n -> M_A / (M_A ∩ M_N),
193+
# i.e. vectors (a1,...,an) such that a1*col1(A) + ... + an*coln(A) ∈ Im(N).
194+
M_A = Singular.Module(A)
195+
M_N = Singular.Module(N)
196+
S = Singular.modulo(M_A, M_N)
197+
if Singular.iszero(S)
198+
R = Singular.base_ring(A)
199+
return Singular.zero_matrix(R, Singular.ncols(A), 0)
200+
else
201+
return Singular.Matrix(S)
202+
end
203+
end
204+
205+
## BasisOfRows / BasisOfColumns
206+
207+
function MatricesForHomalg.BasisOfColumns(A::Singular.smatrix)
208+
M = Singular.Module(A)
209+
G = Singular.std(M)
210+
if Singular.iszero(G)
211+
R = Singular.base_ring(A)
212+
return Singular.zero_matrix(R, Singular.nrows(A), 0)
213+
else
214+
return Singular.Matrix(G)
215+
end
216+
end
217+
218+
function MatricesForHomalg.BasisOfRows(A::Singular.smatrix)
219+
return Singular.transpose(MatricesForHomalg.BasisOfColumns(Singular.transpose(A)))
220+
end
221+
222+
## DecideZeroRows / DecideZeroColumns
223+
224+
function MatricesForHomalg.DecideZeroColumns(B::Singular.smatrix, A::Singular.smatrix)
225+
M_A = Singular.Module(A)
226+
G = Singular.std(M_A)
227+
M_B = Singular.Module(B)
228+
R = Singular.reduce(M_B, G)
229+
return Singular.Matrix(R)
230+
end
231+
232+
function MatricesForHomalg.DecideZeroRows(B::Singular.smatrix, A::Singular.smatrix)
233+
return Singular.transpose(MatricesForHomalg.DecideZeroColumns(Singular.transpose(B), Singular.transpose(A)))
234+
end
235+
236+
## LeftDivide / RightDivide (two-argument: solve AX = B)
237+
238+
function MatricesForHomalg.SafeLeftDivide(A::Singular.smatrix, B::Singular.smatrix)
239+
M_A = Singular.Module(A)
240+
M_B = Singular.Module(B)
241+
T, rest = Singular.lift(M_A, M_B)
242+
if !Singular.iszero(rest)
243+
error("Unable to solve linear system")
244+
end
245+
return Singular.Matrix(T)
246+
end
247+
248+
function MatricesForHomalg.LeftDivide(A::Singular.smatrix, B::Singular.smatrix)
249+
try
250+
return MatricesForHomalg.SafeLeftDivide(A, B)
251+
catch
252+
return "fail"
253+
end
254+
end
255+
256+
function MatricesForHomalg.SafeRightDivide(B::Singular.smatrix, A::Singular.smatrix)
257+
return Singular.transpose(MatricesForHomalg.SafeLeftDivide(Singular.transpose(A), Singular.transpose(B)))
258+
end
259+
260+
function MatricesForHomalg.RightDivide(B::Singular.smatrix, A::Singular.smatrix)
261+
try
262+
return MatricesForHomalg.SafeRightDivide(B, A)
263+
catch
264+
return "fail"
265+
end
266+
end
267+
268+
## LeftDivide / RightDivide (three-argument: solve AX + LY = B)
269+
270+
function MatricesForHomalg.SafeLeftDivide(A::Singular.smatrix, B::Singular.smatrix, L::Singular.smatrix)
271+
R = Singular.base_ring(A)
272+
nr_rows = Singular.nrows(A)
273+
nr_cols_a = Singular.ncols(A)
274+
nr_cols_l = Singular.ncols(L)
275+
# Combine columns of A and L into one matrix
276+
AL = Singular.zero_matrix(R, nr_rows, nr_cols_a + nr_cols_l)
277+
for i in 1:nr_rows, j in 1:nr_cols_a
278+
AL[i, j] = A[i, j]
279+
end
280+
for i in 1:nr_rows, j in 1:nr_cols_l
281+
AL[i, nr_cols_a + j] = L[i, j]
282+
end
283+
M_AL = Singular.Module(AL)
284+
M_B = Singular.Module(B)
285+
T, rest = Singular.lift(M_AL, M_B)
286+
if !Singular.iszero(rest)
287+
error("Unable to solve linear system")
288+
end
289+
T_mat = Singular.Matrix(T)
290+
# Extract the first nr_cols_a rows (corresponding to A)
291+
result = Singular.zero_matrix(R, nr_cols_a, Singular.ncols(T_mat))
292+
for i in 1:nr_cols_a, j in 1:Singular.ncols(T_mat)
293+
result[i, j] = T_mat[i, j]
294+
end
295+
return result
296+
end
297+
298+
function MatricesForHomalg.LeftDivide(A::Singular.smatrix, B::Singular.smatrix, L::Singular.smatrix)
299+
try
300+
return MatricesForHomalg.SafeLeftDivide(A, B, L)
301+
catch
302+
return "fail"
303+
end
304+
end
305+
306+
function MatricesForHomalg.SafeRightDivide(B::Singular.smatrix, A::Singular.smatrix, L::Singular.smatrix)
307+
return Singular.transpose(MatricesForHomalg.SafeLeftDivide(Singular.transpose(A), Singular.transpose(B), Singular.transpose(L)))
308+
end
309+
310+
function MatricesForHomalg.RightDivide(B::Singular.smatrix, A::Singular.smatrix, L::Singular.smatrix)
311+
try
312+
return MatricesForHomalg.SafeRightDivide(B, A, L)
313+
catch
314+
return "fail"
315+
end
316+
end
317+
318+
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)