@@ -62,20 +62,40 @@ function BorderBasis{StaircaseDependence}(b::BorderBasis{LinearDependence})
6262 return BorderBasis (d, b. matrix[rows, cols])
6363end
6464
65+ struct StaircaseSolver{
66+ T,
67+ R<: RankCheck ,
68+ M<: SemialgebraicSets.AbstractMultiplicationMatricesSolver ,
69+ }
70+ max_partial_iterations:: Int
71+ max_iterations:: Int
72+ rank_check:: R
73+ solver:: M
74+ end
75+ function StaircaseSolver {T} (;
76+ max_partial_iterations:: Int = 0 ,
77+ max_iterations:: Int = - 1 ,
78+ rank_check:: RankCheck = LeadingRelativeRankTol (Base. rtoldefault (T)),
79+ solver = SS. ReorderedSchurMultiplicationMatricesSolver {T} (),
80+ ) where {T}
81+ return StaircaseSolver {T,typeof(rank_check),typeof(solver)} (
82+ max_partial_iterations,
83+ max_iterations,
84+ rank_check,
85+ solver,
86+ )
87+ end
88+
6589function solve (
6690 b:: BorderBasis{LinearDependence,T} ,
67- solver:: SemialgebraicSets.AbstractMultiplicationMatricesSolver = MultivariateMoments. SemialgebraicSets. ReorderedSchurMultiplicationMatricesSolver{
68- T,
69- }(),
91+ solver:: StaircaseSolver = StaircaseSolver {T} (),
7092) where {T}
7193 return solve (BorderBasis {StaircaseDependence} (b), solver)
7294end
7395
7496function solve (
7597 b:: BorderBasis{<:StaircaseDependence,T} ,
76- solver:: SemialgebraicSets.AbstractMultiplicationMatricesSolver = MultivariateMoments. SemialgebraicSets. ReorderedSchurMultiplicationMatricesSolver{
77- T,
78- }(),
98+ solver:: StaircaseSolver{T} = StaircaseSolver {T} (),
7999) where {T}
80100 d = b. dependence
81101 dependent = dependent_basis (d)
@@ -180,34 +200,70 @@ function solve(
180200 end
181201 end
182202 @assert o <= length (vars)
183- if o < length (vars)
203+ partial = o < length (vars)
204+ if partial
184205 # Several things could have gone wrong here:
185206 # 1) We could be missing corners,
186207 # 2) We have all corners but we could not complete the border because
187208 # there is not topological order working
188- # In any case, we don't have all multiplication matrices so we abort
189- return
209+ # We now try to build new relation by comparing partial multiplication matrices
210+ # We store them in a vector and reshape in a matrix after as it's easy to append to a vector in-place.
211+ # a matrix after
212+ if solver. max_partial_iterations == 0
213+ return
214+ else
215+ com_fix = partial_commutation_fix (
216+ known_border_coefficients,
217+ border_coefficients,
218+ T,
219+ standard,
220+ vars,
221+ solver. rank_check,
222+ )
223+ end
224+ else
225+ if solver. max_iterations == 0
226+ Uperp = nothing
227+ else
228+ Uperp = commutation_fix (mult, solver. solver. ε)
229+ end
230+ com_fix = if isnothing (Uperp)
231+ nothing
232+ else
233+ Uperp, standard
234+ end
190235 end
191- Uperp = commutation_fix (mult, solver. ε)
192- if isnothing (Uperp)
236+ if isnothing (com_fix)
193237 # The matrices commute, let's simultaneously diagonalize them
194- sols = SS. solve (SS. MultiplicationMatrices (mult), solver)
238+ sols = SS. solve (SS. MultiplicationMatrices (mult), solver. solver )
195239 return ZeroDimensionalVariety (sols)
196240 else
241+ Uperp, Ubasis = com_fix
197242 # The matrices don't commute, let's find the updated staircase and start again
198- new_basis, I1, I2 = MB. merge_bases (standard , dependent)
243+ new_basis, I1, I2 = MB. merge_bases (Ubasis , dependent)
199244 new_matrix = Matrix {T} (undef, length (new_basis), size (Uperp, 2 ))
245+ I_nontrivial_standard = [
246+ _index (Ubasis, std) for
247+ std in standard_basis (b. dependence, trivial = false ). monomials
248+ ]
249+ Uperpstd = Uperp[I_nontrivial_standard, :]
200250 for i in axes (new_matrix, 1 )
201251 if iszero (I1[i])
202252 @assert ! iszero (I2[i])
203- new_matrix[i, :] = b. matrix[:, I2[i]]' * Uperp
253+ new_matrix[i, :] = b. matrix[:, I2[i]]' * Uperpstd
204254 else
205255 @assert iszero (I2[i])
206256 new_matrix[i, :] = Uperp[I1[i], :]
207257 end
208258 end
209259 null = MacaulayNullspace (new_matrix, new_basis)
210- return solve (null, ShiftNullspace {StaircaseDependence} ())
260+ new_solver = StaircaseSolver {T} (;
261+ max_partial_iterations = solver. max_partial_iterations - partial,
262+ max_iterations = solver. max_iterations - ! partial,
263+ solver. rank_check,
264+ solver. solver,
265+ )
266+ return solve (null, ShiftNullspace {StaircaseDependence} (new_solver))
211267 end
212268end
213269
@@ -249,6 +305,117 @@ function commutation_fix(matrices, ε)
249305 end
250306end
251307
308+ function partial_commutation_fix (
309+ known_border_coefficients,
310+ border_coefficients,
311+ :: Type{T} ,
312+ standard,
313+ vars,
314+ rank_check:: RankCheck ,
315+ ) where {T}
316+ function shifted_border_coefficients (mono, shift)
317+ coef = border_coefficients (mono)
318+ ret = zero (coef)
319+ unknown = zero (MP. polynomial_type (mono, T))
320+ for i in eachindex (coef)
321+ if iszero (coef)
322+ continue
323+ end
324+ shifted = shift * standard. monomials[i]
325+ j = _index (standard, shifted)
326+ if ! isnothing (j)
327+ ret[j] += coef[i]
328+ elseif known_border_coefficients (shifted)
329+ ret .+ = coef[i] .* border_coefficients (shifted)
330+ else
331+ MA. operate! (MA. add_mul, unknown, coef[i], shifted)
332+ end
333+ end
334+ return ret, unknown
335+ end
336+ new_relations = T[]
337+ unknowns = MP. polynomial_type (prod (vars), T)[]
338+ for std in standard. monomials
339+ for x in vars
340+ mono_x = x * std
341+ if ! known_border_coefficients (mono_x)
342+ # FIXME what do we do if one of the two monomials is unknown
343+ # but the other one is known ?
344+ continue
345+ end
346+ for y in vars
347+ mono_y = y * std
348+ if ! known_border_coefficients (mono_y)
349+ # FIXME what do we do if one of the two monomials is unknown
350+ # but the other one is known ?
351+ continue
352+ end
353+ if isnothing (_index (standard, mono_x))
354+ if isnothing (_index (standard, mono_y))
355+ coef_xy, unknowns_xy =
356+ shifted_border_coefficients (mono_y, x)
357+ else
358+ mono_xy = x * mono_y
359+ if known_border_coefficients (mono_xy)
360+ coef_xy = border_coefficients (mono_xy)
361+ unknowns_yx = zero (PT)
362+ else
363+ coef_xy = zeros (length (standard. monomials))
364+ unknowns_xy = mono_xy
365+ end
366+ end
367+ coef_yx, unknowns_yx =
368+ shifted_border_coefficients (mono_x, y)
369+ else
370+ if ! isnothing (_index (standard, mono_y))
371+ # Let `f` be `known_border_coefficients`.
372+ # They are both standard so we'll get
373+ # `f(y * mono_x) - f(x * mono_y)`
374+ # which will give a zero column, let's just ignore it
375+ continue
376+ end
377+ mono_yx = y * mono_x
378+ if known_border_coefficients (mono_yx)
379+ coef_yx = border_coefficients (mono_yx)
380+ unknowns_yx = zero (PT)
381+ else
382+ coef_yx = zeros (length (standard. monomials))
383+ unknowns_yx = mono_yx
384+ end
385+ coef_xy, unknowns_xy =
386+ shifted_border_coefficients (mono_y, x)
387+ end
388+ append! (new_relations, coef_xy - coef_yx)
389+ push! (unknowns, unknowns_xy - unknowns_yx)
390+ end
391+ end
392+ end
393+ standard_part = reshape (
394+ new_relations,
395+ length (standard. monomials),
396+ div (length (new_relations), length (standard. monomials)),
397+ )
398+ unknown_monos = MP. merge_monomial_vectors (MP. monomials .(unknowns))
399+ unknown_part = Matrix {T} (undef, length (unknown_monos), length (unknowns))
400+ for i in eachindex (unknowns)
401+ unknown_part[:, i] = MP. coefficients (unknowns[i], unknown_monos)
402+ end
403+ basis, I1, I2 = MB. merge_bases (standard, MB. MonomialBasis (unknown_monos))
404+ M = Matrix {T} (undef, length (basis. monomials), size (standard_part, 2 ))
405+ for i in eachindex (basis. monomials)
406+ if iszero (I1[i])
407+ @assert ! iszero (I2[i])
408+ M[i, :] = unknown_part[I2[i], :]
409+ else
410+ @assert iszero (I2[i])
411+ M[i, :] = standard_part[I1[i], :]
412+ end
413+ end
414+ F = LinearAlgebra. svd (M, full = true )
415+ r = rank_from_singular_values (F. S, rank_check)
416+ return F. U[:, (r+ 1 ): end ], basis
417+ end
418+
252419"""
253420 Base.@kwdef struct AlgebraicBorderSolver{
254421 D,
0 commit comments