Does using GSL.jl mean I have to release my package under GPL?

I’m currently writing a project/package that has using GSL in its source file and GSL = "92c[...]" under Project.toml’s [deps] section. GSL.jl is the wrapper for the GNU Scientific Library (GSL), which is explicitly part of the GNU project, and both GSL and GSL.jl are released under the GPL. I’m inclined to release my project under the MIT License, but I’m unsure if that’s legally allowed. I’ve read through the GNU page for GPL associated works, and derivatives/conveyances, and a lot of it works around providing binaries as well, and the terms around source distribution seems murky to me. However, I’ll only be releasing the .jl source files in GitHub (and hopefully submitting the package to the Julia General registry), and I’m not sure what licensing requirements I must adhere to.
My work is of particular interest to academics. While I’m not opposed to releasing under GPL, it may limit what people can do with my code, because it may also come in proprietary use.

Pages I’ve checked for an answer:

TL;DR:

  • Using a GPL v3 Licensed Julia library as a dependency
  • Want to release my code under MIT License
  • Possible?

As an alternative, the only functionality I need from GSL.jl is its ability to compute associated Legendre polynomials and its first and second derivatives for many (,m) at once, so if someone knows of an alternative with more compatible licensing, please let me know! :slight_smile:

I am afraid that the chances are low to get legal advice in this forum. Sorry!

2 Likes

Pretty much no answer given here or on another forum such as stackexchange is going to be 100% accurate, because what exactly is copyrightable depends on your jurisdiction & what a judge presiding over an eventual case rules. E.g. under EU law, there are certain exceptions (see here) that can be viewed as “virality as the FSF claims it to exist doesn’t work in the EU”. Whether that applies to you is up to your discretion, and if there are too many doubts, asking an actual IP lawyer familiar with your jurisdiction is going to be more fruitful. Alternatively, going with the license of GSL.jl is a safe bet.

1 Like

You can look at what the FSF has written with the benefit of legal counsel and years of experience with their licenses. According to them, the MIT (“expat”) License is compatible with the GPL, so this is allowed. According to the FSF’s GPL FAQ:

If a library is released under the GPL (not the LGPL), does that mean that any software which uses it has to be under the GPL or a GPL-compatible license? Yes, because the program actually links to the library. As such, the terms of the GPL apply to the entire combination. The software modules that link with the library may be under various GPL compatible licenses, but the work as a whole must be licensed under the GPL. [emphasis added]

So your package, which links to the GPL’ed library (GSL in this case) can be under any GPL-compatible license (e.g. MIT), but the software as a whole (your code + GSL) is governed by the union of all the license terms (= GPL, since the GPL restrictions are a strict superset of the MIT license).

So, for example, if Microsoft wanted to use your package in the next version of Excel, they would pull in the GSL dependency (assuming your software doesn’t work without it) and hence the GPL terms. On the other hand, if they were to take your code and then replace the GSL calls with some equivalent replacement under a different license, then they would probably only have to worry about your code’s license (e.g. MIT).

IANAL, and even the FSF’s legal opinion cannot be 100% certain (in an actual court case there is some uncertainty in what a judge/jury will decide), but the FSF has a few decade’s experience in enforcing the GPL so their interpretation seems like a reasonably good bet.

PS. When creating a package that has a GPL dependency, in my opinion it is sometimes clearer to put your package under the GPL too, to make sure the package users don’t miss the GPL requirements (though this prevents the users from modifying your package to strip out the GPL by replacing the GSL calls with something else). Even if you use the MIT license for your own code, I would clearly mention that you have a GPL dependency. However, this has already been done for the GSL.jl package (which is GPL licensed, like the underlying GSL C library).

PPS. Out of curiosity, which GSL function(s) are you using? A lot of GSL functions have equivalent or better replacements elsewhere in the Julia ecosystem.

PPPS. The general principles here stem from copyright law in general, not just for GPL code. If you write software that combines code derived from A, B, and C, then (US) copyright law tells you that the combined work needs to abide by the union of the terms of A, B, and C. Some terms are less restrictive than the GPL (e.g. MIT license) and some are more restrictive than the GPL (e.g. proprietary software). Some terms are mutually incompatible (e.g. the GPL license for A and a proprietary license for B), making it impossible to distribute the combined work because you can’t simultaneously abide by all of the license terms — this is why some licenses are “GPL-incompatible”. The more permissive the licenses are (e.g. MIT and 3-clause BSD are two of the most permissive), of course, the easier abiding by all of the terms becomes and the less likely they are to be incompatible.

6 Likes

Thanks! The problem I’d been having was with “linking”, and how distribution works with source vs. binary distribution, and I couldn’t properly distinguish between legal/colloquial linking and linking in the compilation sense.

The main GSL function I’m using is gsl_sf_legendre_deriv2_array_e, which calculates the associated Legendre polynomial and its first and second derivatives for multiple (,m). I’ve looked at LegendrePolynomials.jl and AssociatedLegendrePolynomials.jl, but neither of them provides the functionality for calculating derivatives out of the box for the associated Legendre functions. Perhaps when I have the time, I can contribute the code for that haha :sweat_smile:. Afaik, GSL also handles different normalizations for the associated Legendre polynomials really well, and LegendrePolynomials.jl does too, but it becomes thorny when derivatives are involved.

They don’t need to. Since they are implemented with type-generic Julia code (presumably just recurrence relations), ForwardDiff.jl can differentiate them just fine:

julia> using LegendrePolynomials, ForwardDiff

julia> Plm(0.5, 3, 2) # associated Legendre polynomial
5.624999999999997

julia> Plm′(x, ℓ, m) = ForwardDiff.derivative(x -> Plm(x, ℓ, m), x) # its derivative
Plm′ (generic function with 1 method)

julia> Plm′(0.5, 3, 2)
3.7499999999999973

As you’d hope, evaluating the derivative is basically the same cost as evaluating the function:

julia> using BenchmarkTools

julia> @btime Plm(0.5, 3, 2);
  132.144 ns (0 allocations: 0 bytes)

julia> @btime Plm′(0.5, 3, 2);
  134.959 ns (0 allocations: 0 bytes)

However, LegendrePolynomials.jl seems to be leaving some performance (and accuracy?) on the table (slow compared to AssociatedLegendrePolynomials.jl and to GSL.jl · Issue #18 · jishnub/LegendrePolynomials.jl · GitHub). AssociatedLegendrePolynomials.jl is faster:

julia> import AssociatedLegendrePolynomials

julia> @btime AssociatedLegendrePolynomials.Plm(3, 2, 0.5)
  34.540 ns (6 allocations: 96 bytes)
5.625

julia> @btime ForwardDiff.derivative(x -> AssociatedLegendrePolynomials.Plm(3, 2, x), 0.5)
  54.234 ns (6 allocations: 192 bytes)
3.75

but this is also losing some performance because it allocates, even if you use its “in-place” API:

julia> P = fill(NaN); # 0-dimensional array to hold the result

julia> @btime AssociatedLegendrePolynomials.Plm!($P, 3, 2, 0.5)
  30.920 ns (5 allocations: 80 bytes)
0-dimensional Array{Float64, 0}:
5.625

GSL is currently slightly faster than AssociatedLegendrePolynomials.Plm!

julia> import GSL

julia> @btime GSL.sf_legendre_Plm(3, 2, 0.5)
  27.088 ns (0 allocations: 0 bytes)
5.625

They are probably all using the same recurrences, but LegendrePolynomials.jl is hiding it behind an iterator-based implementation that I’m guessing is failing to inline or something?

4 Likes