Skip to content

Accept the empty set as linearly independent. #1901 - #1902

Merged
sangwinc merged 1 commit into
devfrom
iss1901
Oct 14, 2026
Merged

sangwinc merged 1 commit into
devfrom
iss1901

Conversation

@sangwinc

Copy link
Copy Markdown
Member

No description provided.

@LukeLongworth

Copy link
Copy Markdown
Collaborator

Kia ora @sangwinc,

This looks sensible to me! I had never considered what a basis for {0} would look like, so this seems like an oversight by me rather than a deliberate decision. I don't think this will break any of my questions (though obviously I can't speak for others).

The only thing that made me pause was whether subspace_equivp({}, [c(0,0)]) and similar should return true. It's probably correct in most cases, though I am a little uncomfortable with the fact that we have no way to track the dimension of the vector space that contains a subspace with basis {}. Is span(c(0,0)) an equivalent subspace to span(c(0,0,0))? They both share the same basis in this case!

Perhaps we could follow a similar strategy to the one I employed in vectorgeometry.mac. There we have an optional input allow_zero so that vector_in_spacep(0, basis) returns false in the case where we want to know if the student has identified a valid direction vector in the affine subspace and vector_in_spacep(0, basis, true) returns true if we just want to know whether the vector is in the subspace at all. In this case I'd probably do something like subspace_equivp(ex,ta,[exclude_empty]) where exclude_empty is assumed false if not given. It's times like this that I wish we had a more Python-esque ability to use optional default function inputs.

@adamant-pwn

Copy link
Copy Markdown
Collaborator

Hi Chris,

The empty family should indeed be linearly independent and span the zero subspace, as long as it is empty family from the same vector space (if they're represented by matrices, column spaces of $2 \times 0$ and $3 \times 0$ matrices are actually distinct).

In general, I much prefer it when we have some general description, and corner cases follow it automatically... In our case, it would mean that empty matrices are taken proper care of by Maxima, which is unfortunately not the case yet, so we'd indeed need to do it on STACK level if we want to support them... Some details below.


Maxima support

The real space of objects we care about in linear algebra is $\mathcal{L}(\mathbb R^n, \mathbb R^m)$, and $\mathbb R^{m \times n}$ is just a proxy to it. From this perspective, it is important to distinguish $n\times0$ and $0\times m$ matrices. As collections of columns:

  • An $n\times0$ matrix contains no vectors, so its columns are independent.
  • A $0\times m$ matrix contains $m$ zero vectors, so its columns are dependent when $m>0$.

With dimensions preserved, the usual rank(A) = number_of_columns(A) test handles both naturally.

Currently, transposing matrix([],[],[]) produces matrix(), losing its dimensions and breaking double transpose. amatrix preserves dimensions, but also has bugs and isn't directly supported by native functions such as rank.

I've submitted proposed fixes upstream:

Until this is resolved, comprehensive support would require quite a few STACK workarounds. I'm not sure the practical benefit justifies maintaining those ourselves. Supporting an empty basis in these predicates seems useful, though.

Related pre-existing issues

These occur both before and after this PR:

  • Empty and invalid inputs are conflated: cols_to_matrix([]) and cols_to_matrix(1) both return matrix([null]). This forces callers to inspect the original input.
  • Empty vectors are mistaken for an empty family: lin_indp([[]]) and lin_indp([[],[]]) return true. These should be dependent: they contain zero vectors. Conversion to matrix() loses the number of vectors.
  • Span comparison is asymmetric: for E: matrix([],[]) and Z: matrix([0],[0]), subspace_equivp(E,Z) returns true, but reversing the arguments returns false. Both should represent the zero subspace of $\mathbb R^2$.
  • Removing dependent columns can lose the ambient dimension: remove_dep(matrix([0],[0])) returns [], which then makes basisify(matrix([0],[0])) fail.

Suggestions for this PR

  1. Document what an unspecified ambient dimension means. I share Luke's concern: {} now matches zero subspaces in both $\mathbb R^2$ and $\mathbb R^3$, although those explicitly dimensioned inputs don't match each other. We could say that {} inherits its ambient space from the question or other argument. An exclude_empty flag could control accepted answers, but wouldn't recover the missing dimension.

  2. Add tests distinguishing empty families from empty vectors, including [], [[]], [[],[]], and matrix([],[]), and test span comparisons in both argument orders.

  3. Clarify the single-row convention in make_list_of_lists. The _op1 fix exposes an existing ambiguity: matrix([1,2,3]) becomes [[1,2,3]], although the documentation says matrices are converted column-wise. Consequently, lin_indp(M) is false, but lin_indp(make_list_of_lists(M)) is true. Similarly, matrix([]) becomes [[]], while matrix([],[]) becomes [].

I wouldn't make fixing all of this a prerequisite for the PR. Longer term, though, I'd prefer one conversion interface that preserves dimensions and distinguishes invalid input from a valid empty family, so the usual rank algorithms work without separate exceptions in every caller.

@sangwinc

Copy link
Copy Markdown
Member Author

Thanks @adamant-pwn and @LukeLongworth for your speedy comments. I'll merge in this minor improvement, and the support for n*0 matrices, etc, can follow in due course.

@sangwinc
sangwinc merged commit 799684f into dev Oct 14, 2026
10 checks passed
@sangwinc
sangwinc deleted the iss1901 branch October 14, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants