Skip to content

Commit

Permalink
Convert this into a standalone library. (#2)
Browse files Browse the repository at this point in the history
- Got rid of the nested namespaces in favor of simpler function calls.
- Accept a reference to a tatami::Matrix instead of a pointer.
- Added an install check workflow.
  • Loading branch information
LTLA authored Jul 16, 2024
1 parent 78b1e87 commit 03d7aff
Show file tree
Hide file tree
Showing 14 changed files with 528 additions and 366 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/check-install.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
on:
push:
branches:
- master
pull_request:

name: Check CMake install

jobs:
install:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Get latest CMake
uses: lukka/get-cmake@latest

- name: Configure the build
run: cmake -S . -B build -DSCRAN_PCA_TESTS=OFF

- name: Install the library
run: sudo cmake --install build

- name: Test downstream usage
run: |
mkdir _downstream
touch _downstream/source.cpp
cat << EOF > _downstream/CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(test_install)
add_executable(whee source.cpp)
find_package(libscran_scran_pca)
target_link_libraries(whee libscran::scran_pca)
EOF
cd _downstream && cmake -S . -B build
71 changes: 64 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,77 @@
cmake_minimum_required(VERSION 3.14)

project(scran_principal_component_analysis
project(scran_pca
VERSION 1.0.0
DESCRIPTION "Principal components analysis for single-cell data"
LANGUAGES CXX)

add_library(scran_principal_component_analysis INTERFACE)
target_compile_features(scran_principal_component_analysis INTERFACE cxx_std_17)
target_include_directories(scran_principal_component_analysis INTERFACE include/scran)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

add_subdirectory(extern)
target_link_libraries(scran_principal_component_analysis INTERFACE scran_core_utils tatami::tatami tatami::tatami_stats ltla::irlba Eigen3::Eigen)
# Library
add_library(scran_pca INTERFACE)
add_library(libscran::scran_pca ALIAS scran_pca)

target_include_directories(scran_pca INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/scran_pca>)
target_compile_features(scran_pca INTERFACE cxx_std_17)

# Dependencies
option(SCRAN_PCA_FETCH_EXTERN "Automatically fetch scran_pca's external dependencies." ON)
if(SCRAN_PCA_FETCH_EXTERN)
add_subdirectory(extern)
else()
find_package(tatami_tatami 3.0.0 CONFIG REQUIRED)
find_package(tatami_tatami_stats 1.0.0 CONFIG REQUIRED)
find_package(libscran_scran_blocks 1.0.0 CONFIG REQUIRED)
find_package(ltla_irlba 2.0.0 CONFIG REQUIRED)
find_package(Eigen3 3.4.0 REQUIRED NO_MODULE)
endif()

target_link_libraries(
scran_pca INTERFACE
tatami::tatami
tatami::tatami_stats
libscran::scran_blocks
ltla::irlba
Eigen3::Eigen
)

# Tests
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
option(SCRAN_PCA_TESTS "Build scran_pca's test suite." ON)
else()
option(SCRAN_PCA_TESTS "Build scran_pca's test suite." OFF)
endif()

if(SCRAN_PCA_TESTS)
include(CTest)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
endif()
endif()

# Install
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/scran_pca)

install(TARGETS scran_pca
EXPORT scran_pcaTargets)

install(EXPORT scran_pcaTargets
FILE libscran_scran_pcaTargets.cmake
NAMESPACE libscran::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libscran_scran_pca)

configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/libscran_scran_pcaConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libscran_scran_pca)

write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/libscran_scran_pcaConfigVersion.cmake"
COMPATIBILITY SameMajorVersion)

install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libscran_scran_pcaConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/libscran_scran_pcaConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libscran_scran_pca)
66 changes: 47 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Principal components analysis, duh

![Unit tests](https://github.com/libscran/principal_component_analysis/actions/workflows/run-tests.yaml/badge.svg)
![Documentation](https://github.com/libscran/principal_component_analysis/actions/workflows/doxygenate.yaml/badge.svg)
[![Codecov](https://codecov.io/gh/libscran/principal_component_analysis/graph/badge.svg?token=qklLZtJSE9)](https://codecov.io/gh/libscran/principal_component_analysis)
![Unit tests](https://github.com/libscran/scran_pca/actions/workflows/run-tests.yaml/badge.svg)
![Documentation](https://github.com/libscran/scran_pca/actions/workflows/doxygenate.yaml/badge.svg)
[![Codecov](https://codecov.io/gh/libscran/scran_pca/graph/badge.svg?token=qklLZtJSE9)](https://codecov.io/gh/libscran/scran_pca)

## Overview

Expand All @@ -16,13 +16,13 @@ factored out into a separate C++ library for easier re-use.
Given a [`tatami::Matrix`](https://github.com/tatami-inc/tatami), the `simple_pca::compute()` function will compute the PCA to obtain a low-dimensional representation of the cells:

```cpp
#include "scran/simple_pca.hpp"
#include "scran_pca/scran_pca.hpp"

tatami::Matrix<double, int>* ptr = some_data_source();
const tatami::Matrix<double, int>& mat = some_data_source();

// Take the top 20 PCs:
scran::simple_pca::Options opt;
auto res = scran::simple_pca::compute(ptr, 20, opt);
scran_pca::SimplePcaOptions opt;
auto res = scran_pca::simple_pca(mat, 20, opt);

res.components; // rows are PCs, columns are cells.
res.rotation; // rows are genes, columns correspond to PCs.
Expand All @@ -45,8 +45,8 @@ This ensures that the inter-block differences do not contribute to the first few
```cpp
std::vector<int> blocks = some_blocks();

scran::blocked_pca::Options bopt;
auto bres = scran::blocked_pca::compute(ptr, blocks.data(), 20, bopt);
scran_pca::BlockedPcaOptions bopt;
auto bres = scran_pca::blocked_pca(mat, blocks.data(), 20, bopt);

bres.components; // rows are PCs, columns are cells.
bres.center; // rows are blocks, columns are genes.
Expand All @@ -59,31 +59,59 @@ we can use `blocked_pca::compute()` to obtain an appropriate matrix that focuses

```cpp
bopt.components_from_residuals = false;
auto bres2 = scran::blocked_pca::compute(ptr, blocks.data(), 20, bopt);
auto bres2 = scran_pca::blocked_pca(mat, blocks.data(), 20, bopt);
```

Check out the [reference documentation](https://libscran.github.io/principal_component_analysis) for more details.
Check out the [reference documentation](https://libscran.github.io/scran_pca) for more details.

## Building projects

This repository is part of the broader [**libscran**](https://github.com/libscran/libscran) library,
so users are recommended to use the latter in their projects.
**libscran** developers should just use CMake with `FetchContent`:
### CMake with `FetchContent`

If you're using CMake, you just need to add something like this to your `CMakeLists.txt`:

```cmake
include(FetchContent)
FetchContent_Declare(
scran_principal_component_analysis
GIT_REPOSITORY https://github.com/libscran/principal_component_analysis
scran_pca
GIT_REPOSITORY https://github.com/libscran/scran_pca
GIT_TAG master # or any version of interest
)
FetchContent_MakeAvailable(scran_principal_component_analysis)
FetchContent_MakeAvailable(scran_pca)
```

Then you can link to **scran_pca** to make the headers available during compilation:

```cmake
# For executables:
target_link_libraries(myexe scran_principal_component_analysis)
target_link_libraries(myexe libscran::scran_pca)
# For libaries
target_link_libraries(mylib INTERFACE scran_principal_component_analysis)
target_link_libraries(mylib INTERFACE libscran::scran_pca)
```

### CMake with `find_package()`

```cmake
find_package(libscran_scran_pca CONFIG REQUIRED)
target_link_libraries(mylib INTERFACE libscran::scran_pca)
```

To install the library, use:

```sh
mkdir build && cd build
cmake .. -DSCRAN_PCA_TESTS=OFF
cmake --build . --target install
```

By default, this will use `FetchContent` to fetch all external dependencies.
If you want to install them manually, use `-DSCRAN_PCA_FETCH_EXTERN=OFF`.
See the tags in [`extern/CMakeLists.txt`](extern/CMakeLists.txt) to find compatible versions of each dependency.

### Manual

If you're not using CMake, the simple approach is to just copy the files in `include/` - either directly or with Git submodules - and include their path during compilation with, e.g., GCC's `-I`.
This requires the external dependencies listed in [`extern/CMakeLists.txt`](extern/CMakeLists.txt), which also need to be made available during compilation.
10 changes: 10 additions & 0 deletions cmake/Config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)
find_dependency(tatami_tatami 3.0.0 CONFIG REQUIRED)
find_dependency(tatami_tatami_stats 1.0.0 CONFIG REQUIRED)
find_dependency(libscran_scran_blocks 1.0.0 CONFIG REQUIRED)
find_dependency(ltla_irlba 2.0.0 CONFIG REQUIRED)
find_dependency(Eigen3 3.4.0 REQUIRED NO_MODULE)

include("${CMAKE_CURRENT_LIST_DIR}/libscran_scran_pcaTargets.cmake")
8 changes: 4 additions & 4 deletions extern/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ FetchContent_Declare(
)

FetchContent_Declare(
scran_core_utils
GIT_REPOSITORY https://github.com/libscran/core_utils
GIT_TAG master
scran_blocks
GIT_REPOSITORY https://github.com/libscran/scran_blocks
GIT_TAG master # ^1.0.0
)

FetchContent_MakeAvailable(tatami)
FetchContent_MakeAvailable(tatami_stats)
FetchContent_MakeAvailable(irlba)
FetchContent_MakeAvailable(eigen)
FetchContent_MakeAvailable(scran_core_utils)
FetchContent_MakeAvailable(scran_blocks)
15 changes: 0 additions & 15 deletions include/scran/scran.hpp

This file was deleted.

Loading

0 comments on commit 03d7aff

Please sign in to comment.