Skip to content

Start GAP with prescribed package versions - #6057

Draft
ThomasBreuer wants to merge 2 commits into
gap-system:masterfrom
ThomasBreuer:TB_prescribed_packages
Draft

Start GAP with prescribed package versions#6057
ThomasBreuer wants to merge 2 commits into
gap-system:masterfrom
ThomasBreuer:TB_prescribed_packages

Conversation

@ThomasBreuer

Copy link
Copy Markdown
Contributor

In order to reproduce computations from a GAP session, it is desirable to start GAP with a given set of GAP packages whose exact version numbers are prescribed.

The idea is as follows.

  • In a GAP session, use the new function PackagesLoaded for collecting the names and version numbers of the currently loaded GAP packages.

  • Write this description to a file.

  • Set the new user preference PrescribedPackageVersions, with value the name of that file.

  • Start a new GAP session. The user preference will modify the autoload process such that exactly the GAP packages listed in the file will be loaded, with exactly the listed versions.

This is just a first proposal. There are things to discuss for example:

  • It is not clear that a user preference is the right way to trigger the changed behaviour, but adding a user preference was easier than adding a command line option.
  • Would it be reasonable to prescribe not only the package version but also its path?
  • What else would we like to get from this new feature?

(One motivation is a remark in oscar-system/GAP.jl/issues/1189.)

@ThomasBreuer ThomasBreuer added kind: new feature topic: packages issues or PRs related to package handling, or specific to a package (for packages w/o issue tracker) release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes labels Aug 13, 2025
@ChrisJefferson

Copy link
Copy Markdown
Contributor

I like the idea.

I wonder if at least a warning (and honestly, I'd be tempted to abort GAP, if the point is to ensure reproducability), if one of the package versions we expect to find is missing? Otherwise if someone loads a list of required packages and are missing one of the correct versions, that package just won't exist -- but that might not stop their calculation, just slow it down, if that package was optional.

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

@ChrisJefferson The proposed code turns the prescribed packages (in the prescribed versions, since all other versions lying around get ignored) into needed packages of GAP. Thus one gets an error as soon as the first such package turns out to be not loadable. This is the error which one gets already now if for example the primgrp package cannot be loaded.

@ChrisJefferson

Copy link
Copy Markdown
Contributor

Ah, thanks. I think I didn't read carefully, I thought the packages still got loaded later when requested.

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

I thought the packages still got loaded later when requested

It is not clear what we really want in this respect.

The currently proposed code removes all information about packages that are not in the list of prescribed package versions. It is still possible to add a new package directory later in the GAP session, and then to load packages which can be found there. Shall we forbid this? Or shall we argue that adding a package directory does not happen silently, and users who do this can add other packages on top of the prescribed ones?

@ChrisJefferson

ChrisJefferson commented Aug 20, 2025

Copy link
Copy Markdown
Contributor

A few years ago I would have said don't bother, but now I do have quite a bit of code that tries using PackageManager to install packages, and people have and maintain multiple package directories, and it might be nice to catch that.

However, I don't think that's required from day one, and we can't stop people doing bad things if they really want to in GAP.

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

@james-d-mitchell You had mentioned in a discussion during the recent GAP Days that you would like to get a better control of the loaded GAP packages. Is the current pull request helpful in this respect, and what else would you like to get?

@james-d-mitchell

Copy link
Copy Markdown
Contributor

I think this is a really useful feature to add, thanks @ThomasBreuer

I've tried playing around with this locally, and perhaps I am doing something wrong, but I see the following behaviour. I did:

gap> FileString("~/gap-requirements.txt", PackagesLoaded());
282
gap> Print(PackagesLoaded());
AutoDoc = "2025.05.09"
Digraphs = "1.14.0"
GAPDoc = "1.6.7"
GRAPE = "4.9.2"
IO = "4.9.1"
PackageManager = "1.6.3"
PrimGrp = "3.4.4"
Semigroups = "5.6.1"
SmallGrp = "1.5.4"
Smallsemi = "0.7.2"
TransGrp = "3.6.5"
datastructures = "0.4.0"
genss = "1.6.9"
images = "1.3.3"
orb = "5.0.0"

Added the line:

SetUserPreference("PrescribedPackageVersions", "~/gap-requirements.txt");

Restarting GAP:

 ┌───────┐   GAP 4.15dev-167-g348aa1e built on 2026-04-03 09:54:12+0100
 │  GAP  │   https://www.gap-system.org
 └───────┘   Architecture: aarch64-apple-darwin25-default64-kv10
 Configuration:  gmp 6.3.0, GASMAN, readline
 Loading the library and packages ...
#W BIND_GLOBAL: variable `IsGraph' already has a value
#I  equal requirements in multiple declarations for operation `Vertices'

#W BIND_GLOBAL: variable `Adjacency' already has a value
Syntax warning: Unbound global variable in /Users/jdm3/gap/pkg/semigroups/gap/semigroups/semirms.gi:35
    i := Random(1, NumberSmallGroups(order));
                   ^^^^^^^^^^^^^^^^^
 Packages:   AutoDoc 2025.05.09, datastructures 0.4.0, Digraphs 1.14.0, GAPDoc 1.6.7, genss 1.6.9, GRAPE 4.9.2,
             images 1.3.3, IO 4.9.1, orb 5.0.0, PackageManager 1.6.3, PrimGrp 3.4.4, Semigroups 5.6.1, SmallGrp 1.5.4,
             Smallsemi 0.7.2, TransGrp 3.6.5
 Try '??help' for help. See also '?copyright', '?cite' and '?authors'
gap>

It seems that maybe the order the packages are loaded is getting messed up? Not sure.

It is not clear that a user preference is the right way to trigger the changed behaviour, but adding a user preference was easier than adding a command line option.

I think this is a good compromise.

Would it be reasonable to prescribe not only the package version but also its path?

Yes, this would be really useful. We've had numerous instances where students had multiple versions of package installed and this cased issues. If we could specify which copy of a package to use, this would simplify things enormously.

What else would we like to get from this new feature?

I think the only thing it would be good to see (other than fixing whatever is going wrong in the example above) would be to document how to use the feature:

  1. In GAP do: FileString("path/to/some/file", PackagesLoaded());
  2. Add the line SetUserPreference("PrescribedPackageVersions", "path/to/some/file"); to your gap.ini file. Call WriteGapIniFile if you don't have one already.
  3. Restart GAP.

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

@james-d-mitchell Thanks for your comments.

  • Concerning the warnings you observed:
    Yes, the reason is a different ordering of LoadPackage calls.
    From the viewpoint of reproducibility, this ordering is relevant, thus we have to make sure that PackagesLoaded lists the packages in the order in which they have been loaded in the current session.
    Currently this information seems to be missing, I will change the pull request accordingly.
    (It is good that we noticed this fact, but the warnings indicate missing dependencies.
    For example, if NumberSmallGroups occurs in a file of the Semigroups package then the SmallGrp package is needed by Semigroups. Up to now, this was not a problem because SmallGrp is listed as a needed package of GAP and therefore gets loaded before Semigroups.
    With the proposed mechanism, all packages from the PrescribedPackageVersions list become needed packages of GAP, and currently the order in which they get loaded is different.)

  • Concerning information about package paths:
    On the one hand, this can be useful, perhaps it helps already to tell the user about some surprising package installation paths.
    On the other hand, prescribed paths are not helpful if one tries to repeat a computation on a different computer, and in theory, two installations of a packages with the same version number should behave equally.
    We can provide two variants of PackagesLoaded, one that lists version numbers and paths, one that lists only version numbers.

  • Concerning the documentation:
    Yes, the feature must be documented once we know what exactly it offers.

In order to reproduce computations from a GAP session,
it is desirable to start GAP with a given set of GAP packages
whose exact version numbers are prescribed.

The idea is as follows.

- In a GAP session, use the new function `PackagesLoaded`
  for collecting the names and version numbers of the currently loaded
  GAP packages.

- Write this description to a file.

- Set the new user preference `PrescribedPackageVersions`,
  with value the name of that file.

- Start a new GAP session.
  The user preference will modify the autoload process such that
  exactly the GAP packages listed in the file will be loaded,
  with exactly the listed versions.
When one wants to start a GAP session
with the same package versions loaded as in the previous session,
it can make a difference if these packages are loaded in a different order.

The  information about the order in which packages have been read in the
current session is available via the log messages in
`GAPInfo.PackageLoadingMessages`.
This commit changes `PackagesLoaded` and the format of the value for the
proposed user preference `PrescribedPackageVersions` such that the loading
order is respected when the user preference is active.

Note that the `OnlyNeeded` option is set (and logged) only per `LoadPackage`
call, and sets of dependent packages get loaded together via one
`LoadPackage` call.
Thus we get the intended loading order by storing the packages for which
`LoadPackage` was called, in the right order, and together with the
`OnlyNeeded` value that was set in the reference GAP session.
Thus we cannot simply set `OnleNeeded` to `true` when loading the packages.

(In the list created by `PackagesLoaded()`, the indirectly loaded packages
do not have entries concerning when they get loaded and which `OnlyNeeded`
value is valid for them.)
@ThomasBreuer
ThomasBreuer force-pushed the TB_prescribed_packages branch from 348aa1e to c6bd7cd Compare August 21, 2026 08:24
@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

@james-d-mitchell I have updated the pull request such that PackagesLoaded() produces a list that records also the order in which the packages have been loaded in the current session, such that prescribing this list of packages for a new GAP session will load these packages in the same order.

Achieving this is quite tricky. Since GAP loads each set of interdependent packages together, with only one LoadPackage call, we have to call LoadPackage exactly for those packages for which this was the case in the reference session. Then we rely on the fact that the dependent packages will get loaded in the same order as before. In order to make that work, we have to specify the same OnlyNeeded values for the LoadPackage calls as in the reference session. (The GAP package loading mechanism is complicated.)

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

My previous comment may give the impression that it is a very complicated procedure to prescribe exact package version. However, the aim of the intended feature was to write down such a list in order to get reproducible behaviour.

In this sense, we do not have to force the same behaviour as in some GAP session where the packages were loaded in a particular order that is perhaps "natural" but that is not really necessary for the thousand GAP sessions we want to compare with each other.
That is, we may prescribe a reasonable list of package versions, and disable all other package versions that may be available in some root directory. Then GAP will choose an order to load these packages. Since this will always be the same ordering, we get the desired reproducibility.

So which feature are we interested in: the complicated one that respects the loading order, or the simpler one that doesn't care about the loading order?

@ThomasBreuer

Copy link
Copy Markdown
Contributor Author

@james-d-mitchell Could you have another look at this?

@james-d-mitchell

Copy link
Copy Markdown
Contributor

@james-d-mitchell Could you have another look at this?

@ThomasBreuer I'll have a look today, thanks for the ping!

Comment thread lib/package.gd
## and <C>version</C> is its version.
## <P/>
## One can print this string to a file and set the user preference
## <C>"PrescribedPackageVersions"</C> to the name of this file,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## <C>"PrescribedPackageVersions"</C> to the name of this file,
## <C>"PrescribedPackageVersions"</C> to the name of this file.

Comment thread lib/package.gi
UserPreference( "PrescribedPackageVersions" ) );
if exact <> fail then
# Store the list of prescribed package version,
# including the informtion about ordering and 'OnlyNeeded' option.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# including the informtion about ordering and 'OnlyNeeded' option.
# including the information about ordering and 'OnlyNeeded' option.

Comment thread lib/package.gi
# including the informtion about ordering and 'OnlyNeeded' option.
GAPInfo.PrescribedPackageVersions:= exact;
exact:= List( exact, x -> x{ [ 1, 2 ] } );
GAPInfo.PackagesInfo:= Filtered( GAPInfo.PackagesInfo,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here if the UserPreference contains nonsense? Is there any reasonable way to parse it, and give a meaningful error if it is somehow not well-formed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should have read further, I see this is resolved below.

Comment thread lib/package.gi
exact:= List( exact, x -> x{ [ 1, 2 ] } );
GAPInfo.PackagesInfo:= Filtered( GAPInfo.PackagesInfo,
r -> [ LowercaseString( r.PackageName ), r.Version ] in exact );
fi;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semi-relatedly, shouldn't there be some sort of warning or something if there's a mismatch between the initial value of GAPInfo.PackagesInfo and exact? What if there things in exact not in GAPInfo.PackagesInfo for example?

Comment thread lib/package.gi
fi;
fi;
fi;
return fail;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the comments above would it be possible to maybe give an info warning or error in the case that the file exists but fails the parsing above, to indicate what's wrong?

Comment thread lib/package.gd
## One can print this string to a file and set the user preference
## <C>"PrescribedPackageVersions"</C> to the name of this file,
## Then starting &GAP; anew will load exactly the same packages.
## </Description>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a good idea here to give an explicit example of how you might do this, and to specify that the absolute path of the file is required also.

@james-d-mitchell james-d-mitchell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good, and it appears to work reasonably well. There are three comments:

  1. When I do gap -A after adding
    SetUserPreference("PrescribedPackageVersions", "/Users/jdm3/Library/Preferences/GAP/prescribed.txt"); to my gap.ini file, and that file contains the output of PackagesLoaded(); I get the info warning #I Options stack is already empty
  2. When this fails, it does so silently, it'd be better if it did it loudly, so that one could easily see what has gone wrong and why (in my case I first specified the relative path to the file prescribed.txt which didn't work).
  3. The process of writing to a file the currently loaded packages, adding that to user preference somewhere, etc, might be difficult for beginners, and we could do somethings to make this easier (such as an analogue of WriteGAPIniFile or similar). This could be done after this PR is merged though and is probably out of scope for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind: new feature release notes: use title For PRs: the title of this PR is suitable for direct use in the release notes topic: packages issues or PRs related to package handling, or specific to a package (for packages w/o issue tracker)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants