Skip to content

ENH: Pin vendored Eigen3 fork to tagged release 5.0.1 (closes #6239, supersedes #6247)#6249

Closed
hjmjohnson wants to merge 3 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:eigen-fork-update-verify-2026-05-10
Closed

ENH: Pin vendored Eigen3 fork to tagged release 5.0.1 (closes #6239, supersedes #6247)#6249
hjmjohnson wants to merge 3 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:eigen-fork-update-verify-2026-05-10

Conversation

@hjmjohnson
Copy link
Copy Markdown
Member

@hjmjohnson hjmjohnson commented May 10, 2026

Pin ITK's vendored Eigen3 fork to the tagged Eigen 5.0.1 release (bc3b39870) plus 6 ITK overlay patches, instead of a moving libeigen/master snapshot. Closes #6239 durably; supersedes #6247.

What changed vs the prior round of this PR

Earlier revisions of this PR pinned to for/itk-20260509-599d71ab-p2-prose — a fork tag based on libeigen/master HEAD 599d71ab (2026-05-09). That's a moving point on master, not a release tag. This revision repoints Modules/ThirdParty/Eigen3/UpdateFromUpstream.sh to:

readonly tag="for/itk-eigen-5.0.1"

which is built as: libeigen/eigen 5.0.1 (bc3b39870) + 6 ITK overlay commits cherry-picked on top (same overlay set as the previous master-snapshot tag).

Why a tagged release instead of a master snapshot

SemVer-stable release tags give ITK reproducible upstream versioning. Pinning to a master HEAD inherits any post-5.0.1 development churn (CI lint changes, GPU work, REUSE updates, etc.) that isn't release-blessed. The 5.0.1 baseline is the most recent Eigen point-release and is what downstream consumers will normally pair with ITK builds.

Overlay stack on top of 5.0.1
Commit Purpose
.gitattributes change Relax content checks for lapacke.h
CMakeLists.txt stub INTERFACE-only build for vendored use
README.kitware.md Document fork purpose + local patches
CMakeLists.txt (eigen_internal + Config) Register eigen_internal in ITK main targets; parametrize Eigen3Config.cmake.in
CMakeLists.txt (greptile fixups) Post-import review fixes
CMakeLists.txt (prose budget) Trim install-comment block to ITK prose budget

Tag for/itk-eigen-5.0.1 is published on InsightSoftwareConsortium/eigen.

@hjmjohnson hjmjohnson changed the title ENH: Update vendored Eigen3 fork to for/itk-tag (closes issue 6239, supersedes 6247) ENH: Update vendored Eigen3 fork tag (closes #6239 durably, supersedes #6247) May 10, 2026
@github-actions github-actions Bot added the type:Enhancement Improvement of existing methods or implementation label May 10, 2026
@hjmjohnson hjmjohnson marked this pull request as ready for review May 10, 2026 14:33
@greptile-apps

This comment was marked as resolved.

Comment thread Modules/ThirdParty/Eigen3/src/itkeigen/cmake/FindStandardMathLibrary.cmake Outdated
Comment thread Modules/ThirdParty/Eigen3/src/itkeigen/CMakeLists.txt Outdated
@blowekamp
Copy link
Copy Markdown
Member

Matrix of builds using both vendored and system installed eigen for SimpleITK, BRAINSTools.

SimpleITK has three distinct ways it can consume ITK. The new root build with content fetch, the default superbuild from an installed directory, and superbuild with "ITK_USE_BUILD_DIR". It is important to test using ITK from a build and installation directory.

Additionally, there were some oddities/additional requirements with building a wrapped ITK remote module against an ITK build directory.

This took me a lot of time to get the updated cmake instructor working in these difference configuration and figuring out some issue with ThirdParty libraries export specification. unfortunately, the ThirdParty library would done several different ways and there was not way to document on the intricate details, and there may be leftover incorrect comments in places.

hjmjohnson added a commit to hjmjohnson/eigen that referenced this pull request May 10, 2026
Greptile P1 finding on PR InsightSoftwareConsortium/ITK#6249:
the 8-line comment block at CMakeLists.txt:971-981 and the
3-line block in Eigen3Config.cmake.in:7-9 violate ITK's
prose-budget cap of 1 short line per in-source comment.

Both reduce to a single load-bearing line carrying the issue
reference; the rest of the rationale lives in the commit
message and PR body, per AGENTS.md → prose-budget.md.

Issue:    InsightSoftwareConsortium/ITK#6239
Reviewer: greptile P1 on InsightSoftwareConsortium/ITK#6249
@hjmjohnson hjmjohnson force-pushed the eigen-fork-update-verify-2026-05-10 branch from f4f5880 to 02a74a0 Compare May 10, 2026 16:19
@hjmjohnson
Copy link
Copy Markdown
Member Author

Thanks @blowekamp. Acknowledging the three SimpleITK consumption modes and the wrapped-remote-module-against-build-tree gotcha. Kicking off the matrix locally now; will report results back here.

Test matrix (3 SimpleITK modes × 2 ITK Eigen modes + 1 wrapped-module test)
ID SimpleITK mode ITK source Eigen mode
M1 new root build (FetchContent) (fetched) vendored (this PR's tag)
M2 superbuild from installed dir cmake --install tree vendored
M3 superbuild with ITK_USE_BUILD_DIR build tree vendored
M4 M2 again install tree system Eigen 5.0.1 (homebrew)
M5 M3 again build tree system Eigen 3.4.0 (downloaded)
M6 wrapped remote module ITK build tree (ITK_DIR=...) vendored

Acknowledging your note that the ThirdParty libraries export specification has multiple historical paths and there may be leftover incorrect comments. The current PR contains a follow-up commit (02a74a0) that already trimmed two over-budget comment blocks per greptile's prose-budget finding; if the matrix surfaces additional incorrect-comment hotspots in the install/build/use paths, I'll fix those as fixup commits and call them out explicitly.

Cleanup of the dual install rule

The current if(DEFINED ITK3P_INSTALL_EXPORT_NAME) guard around install(TARGETS eigen_internal EXPORT ${ITK3P_INSTALL_EXPORT_NAME}) makes the fork build standalone (without ITK3P_INSTALL_EXPORT_NAME in scope) a no-op while still landing eigen_internal in ITKTargets.cmake when consumed by ITK proper. If the matrix shows that ITK_USE_BUILD_DIR or wrapped-remote-module paths exercise a different build-tree-side install(EXPORT) rule that needs the same dual treatment, that's another candidate for a follow-up fixup.

Will post matrix results once builds complete.

@hjmjohnson
Copy link
Copy Markdown
Member Author

Build matrix results — three SimpleITK consumption modes against this PR's tip

All three modes Brad called out pass cleanly with the new fork tag content. eigen_internal is correctly registered in ITKTargets.cmake for both the install-tree and build-tree consumer paths.

Mode What SimpleITK build status eigen_internal availability
M1 FetchContent root build SimpleITK CMakeLists.txt → sitkITKFetchContent.cmake (FIND_PACKAGE_ARGS form) ✅ 984/984 targets, 0 errors (verified earlier in PR #6247 thread; content unchanged modulo trimmed comments) n/a (build-tree from FetchContent)
M2 Superbuild from installed ITK cmake -DUSE_SYSTEM_ITK=ON -DITK_DIR=<install>/lib/cmake/ITK-6.0 ✅ Superbuild 29/29 stages, 0 errors, 96 libSimpleITK.a* add_library(ITK::eigen_internal …) in installed ITKTargets.cmake
M3 Superbuild with ITK_USE_BUILD_DIR=ON cmake -DITK_USE_BUILD_DIR=ON -DITK_GIT_REPOSITORY=hjmjohnson/ITK -DITK_GIT_TAG=eigen-fork-update-verify-2026-05-10 ✅ Superbuild 37/37 stages (incl. fresh ITK build from this PR), 0 errors, 50 libSimpleITK.a* add_library(ITK::eigen_internal …) in build-tree ITKTargets.cmake (count=1)

Mechanical detail for M3 (the build-dir consumer Brad noted as the tricky one)

ITK_USE_BUILD_DIR=ON causes SimpleITK Superbuild to:

  • Skip the ITK install step (INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Skipping install step.")
  • Set ITK_DIR to the ExternalProject's BINARY_DIR rather than <INSTALL_DIR>/lib/cmake/ITK

In the build-tree ITKTargets.cmake produced by this configuration, add_library(ITK::eigen_internal INTERFACE IMPORTED) is present (count=1) — the dual export-set install rule in the new fork tag works in build-tree mode for the same reason it works in install-tree mode: the rule is install(TARGETS … EXPORT ${ITK3P_INSTALL_EXPORT_NAME}), and the EXPORT-set is referenced by both ITKTargets.cmake (build-tree generator) and ITKTargets-release.cmake (install-time generator). No special-casing was needed.

Pending: M4-M6

  • M4 (M2 with system Eigen 5.0.1 brew): system-Eigen path was independently verified during PR BUG: Restore eigen_internal export to ITK's main targets set (#6239) #6247 against three configurations (vendored 5.0.1 / system 5.0.1 / system 3.4.0) and reported at ENH: Master tracking — Eigen3 third-party design for ITK 6.x #6230 — system mode bypasses eigen_internal entirely, so this PR's fork-tag change has no effect on that path.
  • M5 (M3 with system Eigen 3.4.0): same — system Eigen bypasses the vendored target.
  • M6 (wrapped remote module against ITK build dir): not yet run; Brad's note about "additional requirements with building a wrapped ITK remote module against an ITK build directory" is acknowledged. Want to pick a specific module to exercise (e.g. ITKVariationalRegistration, ITKMontage, ITKTotalVariation), or is the M3 result sufficient?

Reproducer state preserved

  • M2: /tmp/sitk-m2-installdir/ (Superbuild root, 96 libSimpleITK*.a produced)
  • M3: /tmp/sitk-m3-buildir/ (Superbuild root, 50 libSimpleITK*.a + ITK-build/ subdir consumed in-place)

@hjmjohnson
Copy link
Copy Markdown
Member Author

M6 result — ITKTotalVariation against ITK build directory ✅

Reproduced the canonical "wrapped remote module against ITK build directory" case using ITKTotalVariation PR #57 (@blowekamp's "Update for ITK Interface Modules and use ITK targets and installation"). This is also the canonical proxTV <Eigen/Dense> consumer that PR #6223 was added for.

cmake -G Ninja -S ~/src/ITKTotalVariation -B /tmp/m6-itktotalvariation-bld \
  -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON \
  -DITK_DIR=~/src/ITK-eigen-fork-verify-bld   # ← ITK BUILD tree, not install
cmake --build /tmp/m6-itktotalvariation-bld -j8
result
Configure ✅ 0 errors, 0 warnings
Build ✅ 0 errors, 31/31 stages, exit 0
libitkproxTV-6.0.a ✅ produced at _deps/proxtv_fetch-build/src/ (the proxTV fork that uses bare <Eigen/Dense> via ITKEigen3_INCLUDE_DIRS)
TotalVariationTestDriver linked
TotalVariationHeaderTest1 linked
Test driver path linked into ITK build tree's bin/ (build-tree consumer behavior, no install)

Net build matrix for PR #6249

Mode Status
M1 SimpleITK FetchContent root build ✅ verified earlier (PR #6247 thread, 984/984)
M2 SimpleITK Superbuild from installed ITK ✅ Superbuild 29/29, 96 libSimpleITK*.a, eigen_internal in installed ITKTargets
M3 SimpleITK Superbuild with ITK_USE_BUILD_DIR=ON ✅ Superbuild 37/37, 50 libSimpleITK*.a, eigen_internal in build-tree ITKTargets
M6 ITKTotalVariation against ITK build directory ✅ 31/31 stages, libitkproxTV-6.0.a + TotalVariationTestDriver linked, proxTV <Eigen/Dense> resolution clean

All four consumer modes pass with the new fork tag. eigen_internal lands in ITKTargets.cmake for both build-tree and install-tree variants, and the proxTV <Eigen/Dense> resolution unchanged from the PR #6223 fix.

Reproducer state preserved at /tmp/m6-itktotalvariation-bld/ and ~/src/ITKTotalVariation (on pr57 branch).

@blowekamp
Copy link
Copy Markdown
Member

Why are we using an arbitrary Eigen hash and not a release version?

@hjmjohnson
Copy link
Copy Markdown
Member Author

This got co-mingled with other work testing with latest eigen and forgot to recheck the pinning. That is coming shortly.

kwrobot and others added 3 commits May 11, 2026 09:21
Code extracted from:

    https://github.com/InsightSoftwareConsortium/eigen

at commit 6c0093b842e91dd7e826a48127f5896ef2015c4b (for/itk-eigen-5.0.1).
# By Eigen Upstream
* upstream-Eigen3:
  Eigen3 2026-05-11 (for/itk-eigen-5.0.1)
Repoint Modules/ThirdParty/Eigen3/UpdateFromUpstream.sh from the
moving master-snapshot tag

    for/itk-20260509-599d71ab-p2-prose   (libeigen/master HEAD)

to the SemVer-stable release tag

    for/itk-eigen-5.0.1                  (libeigen 5.0.1 + 6 ITK overlays)

The fork-side tag for/itk-eigen-5.0.1 is built by branching off
libeigen/eigen 5.0.1 (bc3b39870) and cherry-picking the same 6 ITK
overlay patches that the master-snapshot fork tag carried:

  - .gitattributes: relax content checks for lapacke.h
  - CMakeLists.txt: stub for vendored INTERFACE-only use
  - README.kitware.md: fork purpose + local-patches documentation
  - CMakeLists.txt: register eigen_internal in ITK targets +
    parametrize Eigen3Config.cmake.in
  - CMakeLists.txt: post-import greptile fixups
  - CMakeLists.txt: trim eigen_internal install comments to fit
    ITK prose budget

Pinning to a release tag (rather than a master snapshot) gives ITK
stable, reproducible upstream versioning and avoids inheriting any
post-5.0.1 master-branch development changes.
@hjmjohnson hjmjohnson force-pushed the eigen-fork-update-verify-2026-05-10 branch from 02a74a0 to 5a59ac8 Compare May 11, 2026 14:23
@blowekamp
Copy link
Copy Markdown
Member

blowekamp commented May 11, 2026

I ran a diff between this branch and 7aa8ae2 on the itkeigen/CMakeLists.txt filter where things were known to work.

I see the following:

@@ -905,10 +962,44 @@ endif()
 #   #include <itkeigen/Eigen/x>
 # INSTALL: headers require pre-prend itkeigen/Eigen/X.
 target_include_directories (eigen_internal SYSTEM INTERFACE
-  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${ITK3P_INSTALL_INCLUDE_DIR}/itkeigen>;"
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>
+  $<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>
   )

This is not correct. The eigen_internal should support the native eigen includes.

This is wrong here too:

set(
ITKEigen3_INCLUDE_DIRS
${ITKEigen3_SOURCE_DIR}/src
${ITKEigen3_SOURCE_DIR}/src/itkeigen
)

The ITK::ITKEigen3Module should add support for the itkeigen path.

This way direct usage of eigen_internal is equivalent to a normal Eigen Library.

Additionally, the generation of "ITKInternalEigen3Targets.cmake" and ITKInternalEigen3Config.cmake was intentionally removed. There usage was very error prone and problematic in certain cases. Consumers can just use the ITK Eigen targets that are not properly exported in their application. TVProx was updated to do this greatly simplifying the CMake code all around.

@hjmjohnson hjmjohnson changed the title ENH: Update vendored Eigen3 fork tag (closes #6239 durably, supersedes #6247) ENH: Pin vendored Eigen3 fork to tagged release 5.0.1 (closes #6239, supersedes #6247) May 11, 2026
@hjmjohnson
Copy link
Copy Markdown
Member Author

Force-pushed to repin the vendored Eigen3 fork from the master-snapshot tag to the SemVer-stable release tag for/itk-eigen-5.0.1 — libeigen 5.0.1 (bc3b39870) + the same 6 ITK overlay patches.

Title + body updated to reflect the new pinning. CI re-running on the new HEAD.

@hjmjohnson
Copy link
Copy Markdown
Member Author

@blowekamp I'm at the end of my skill base and knowledge base for all the scenarios needed for Eigen. I naively thought this was going to be some minor refinements on an UpdateFromUpstream.sh standard vendored tool update. I hoped that the many modernizations of CMake for Eigen in the 5.0 branch were going to limit the amount of custom CMake variants that are necessary.

I now realize that I should not have attempted to update Eigen. This is not a typical update vendored tool pattern, but one that has many, many patches and potholes and subtle build implementations. Testing the build and testing against BRAINSTools, Slicer, Slicer Extensions, and ANTs is insufficient evidence that the build is correct.

I propose reverting back to Pre Eigen 5.0 (Pre PR #6176) (i.e., the eigen version from a month ago) and move on to the more pressing issues of remote module ingestion.

An update to Eigen 5.0 can happen when more knowledgeable developers have the desire to do the work.

I am sorry that I have taken up so much of your time on this issue. I don't know what to do next.

@hjmjohnson hjmjohnson marked this pull request as draft May 11, 2026 14:47
@blowekamp
Copy link
Copy Markdown
Member

blowekamp commented May 11, 2026

We are this far we might as well keep going. Is there documentation for the process done to update the ITK eigen3 fork? I have not been looking at that closely.

At this point I have reviewed the Eigen CMake code close enough I should be able to restore it to what I had operational before.

A problem with ITK's Eigen is that too many usages were made requirements. For example being able to include two different Eigen libraries in one compilation unit does not seem reasonable.

I neglected to remove this documentation here: https://github.com/InsightSoftwareConsortium/ITK/blob/main/Modules/ThirdParty/Eigen3/src/itk_eigen.h.in#L32-L57
Which is odd and complicated usage.

I propose we finish this update to 5.0.1, which works for most cases. Then I'll clean up the include paths, and such in ITK. I'll make a PR in ITK and testing can be done to verify. When checked, we can some hope put the change into ITK Eigen's fork so it'll stick this time.

@hjmjohnson
Copy link
Copy Markdown
Member Author

@blowekamp Thank you. I do not want to burden you more, but you might be one of the few people who knows what needs to be done to get this right.

Here is what I propose to keep moving forward (ignore any of this that you want, your knowlegebase on this issue far exceeds mine):

  • Modify/change/update UpdateFromUpstream.sh in eigen to be a better gatekeeper of what needs to occur, and provide hints about how to manage the InsightSoftwareConsortium/Eigen repo with respect to branches and tags.
# I think this is what should have happened as a starting point
# Inputs
WORK_TREE=~/src
REPO_DIR=$WORK_TREE/eigen
ITK_CURRENT_TAG=for/itk-20260305-4c99fca       # prior fork tag
ITK_EIGEN_TARGET_TAG=5.0.1                     # new libeigen release
NEW_BRANCH=branch_itk_$ITK_EIGEN_TARGET_TAG
ITK_NEW_TAG=for/itk-eigen-$ITK_EIGEN_TARGET_TAG

# 1. Clone (or reuse) the fork and ensure the tree is clean:
  git clone git@github.com:InsightSoftwareConsortium/eigen.git $REPO_DIR
  cd $REPO_DIR
#  2. Wire remotes:
  git remote add isceigen git@github.com:InsightSoftwareConsortium/eigen.git  # or set-url
  git remote add libeigen git@gitlab.com:libeigen/eigen.git
#  3. Fetch (no --tags on libeigen — historical tag collisions):
  git fetch isceigen
  git fetch libeigen master
#  4. Mirror libeigen/master → isceigen/master:
  git push isceigen refs/remotes/libeigen/master:refs/heads/master
#  5. Mirror the chosen release tag (refuse to move if already on isceigen at a different SHA):
  git fetch libeigen tag $ITK_EIGEN_TARGET_TAG
  git push isceigen tag $ITK_EIGEN_TARGET_TAG
#  6. Fetch the prior ITK fork tag:
  git fetch isceigen refs/tags/$ITK_CURRENT_TAG:refs/tags/$ITK_CURRENT_TAG
#  7. Inspect overlay commits to replay:
  git log --oneline libeigen/master..refs/tags/$ITK_CURRENT_TAG
#  8. Branch from the old fork tag:
  git checkout -b $NEW_BRANCH refs/tags/$ITK_CURRENT_TAG
#  9. Rebase overlays onto the new release tag (resolve conflicts as needed):
  git rebase --onto refs/tags/$ITK_EIGEN_TARGET_TAG libeigen/master $NEW_BRANCH
#  10. Verify:
  git log  --oneline refs/tags/$ITK_EIGEN_TARGET_TAG..HEAD
  git diff --stat    refs/tags/$ITK_EIGEN_TARGET_TAG..HEAD
#  11. Tag the rebased tip and publish (ITK's UpdateFromUpstream.sh consumes this tag):
  git tag -a $ITK_NEW_TAG -m "ITK overlay replayed onto libeigen $ITK_EIGEN_TARGET_TAG (was $ITK_CURRENT_TAG)"
  git push isceigen refs/tags/$ITK_NEW_TAG
  git push isceigen $NEW_BRANCH:$NEW_BRANCH
#  12. In ITK, set in Modules/ThirdParty/Eigen3/UpdateFromUpstream.sh:
  readonly tag="for/itk-eigen-5.0.1"
6c0093b84 ITK: Trim eigen_internal install-rule comments to fit prose budget
8b1c73ea8 ITK: Apply post-import greptile-review fixups to CMakeLists.txt
f5aef6280 ITK: Register eigen_internal in ITK main targets + parametrize Config
13019d7d5 ITK: Add README.kitware.md (fork purpose & local patches)
7d1476716 ITK: Override upstream Eigen3 CMakeLists.txt for vendored use
a76e0c9ba ITK: Relax content-checks for lapacke.h in .gitattributes

=============
If you want help, let me know. I do not want to interfere with anything that you might be working on.

@blowekamp
Copy link
Copy Markdown
Member

Thanks you for the details on how the update should happen.

I am looking at non-merge commit in Modules/ThirdParty/Eigen3/src/itkeigen and there have been quite a few that are not in the ITK fork. The commits likely should be in the ITK fork, where they can be resolved when updating the fork, and not as part of the sub-tree merged in ITK.

66916d64fa COMP: Bump itkeigen cmake_minimum to 3.16.3 and clarify dead-block wrap
f6eb95b9f8 COMP: Address greptile review on vendored Eigen 5 import
6f40e8a5a8 COMP: Use pzero() init for Packet locals in SelfadjointMatrixVector loop
91f62324d4 COMP: Use pzero(Packet{}) for SelfadjointMatrixVector accumulators
da8e99c5a7 COMP: Suppress GCC -Wmaybe-uninitialized in Eigen3 SelfadjointMatrixVector
ed5447249a COMP: ARM Eigen copying an object of non-trivial type

The workflow for working on a forked thirparty I don't think are specified clearly and commits can more easily be managed by reapplying in the fork than as part of conflict resolution during ITK sub-tree merge.

Perhaps we could just switch to using FetchContent from the forked repo instead? This would nearly force the changes into the fork.

Also note that DCMTK has a fork, but no subtree merge. Instead it uses and External Project to install it for usage.

I am currently inspecting histories to better understand....

@hjmjohnson
Copy link
Copy Markdown
Member Author

@blowekamp Take the proposed plan as a suggested starting point, not as some agreed-upon plan. I am certain the way I originally approached the problem was wrong. When retracking my steps, I tried to identify a better approach. Please use the plan I laid out as the alpha draft for a procedure to normalize how Eigen should be updated in the future. (PS. It looks like Eigen is under heavy development right now to remove old coding paradigms, use new C++ features to overcome and simplify the codebase, and hopefully provide performance improvements. I'd guess that we will desire to update Eigen again in a few months to take advantage of those changes.

@blowekamp
Copy link
Copy Markdown
Member

See PR #6259 for continued work.

@hjmjohnson
Copy link
Copy Markdown
Member Author

Superceeded.

@hjmjohnson hjmjohnson closed this May 12, 2026
@hjmjohnson hjmjohnson mentioned this pull request May 12, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:Enhancement Improvement of existing methods or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Eigen3 update (64ddc666) breaks downstream FetchContent+find_package use of ITK — eigen_internal removed from main export set

3 participants