Skip to content

Commit

Permalink
Merge pull request #350 from astro-informatics/tk/cppflow-conan-package
Browse files Browse the repository at this point in the history
Substitute cppflow submodule with conan package
  • Loading branch information
tkoskela authored Jun 21, 2023
2 parents ae4c48e + 976872f commit 2031067
Show file tree
Hide file tree
Showing 17 changed files with 165 additions and 111 deletions.
32 changes: 23 additions & 9 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ jobs:
with:
submodules: recursive

# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}

- name: Install Dependencies on Ubunutu
if: ${{ contains(matrix.os, 'ubuntu') }}
run: |
Expand All @@ -81,12 +76,14 @@ jobs:
- name: Install Dependencies on MacOS
if: ${{ contains(matrix.os, 'macos') }}
run: |
# Skip brew update until https://github.com/actions/setup-python/issues/577 is fixed
# Seems to be working, alghough the issue is still open
brew update
brew install libtiff open-mpi libyaml ccache
echo "CMAKE_PREFIX_PATH=/usr/local/opt/libomp" >> $GITHUB_ENV
# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}

- name: Install Tensorflow API on Ubuntu
# TODO could this be combined with mac version somehow? if/else?
if: ${{ contains(matrix.os, 'ubuntu') }}
Expand Down Expand Up @@ -132,6 +129,23 @@ jobs:
${{ matrix.os }}-${{ matrix.cxx }}
${{ matrix.os }}
- name: Checkout cppflow repo
uses: actions/checkout@v3
with:
repository: UCL/cppflow.git
path: cppflow
ref: master

- name: Create cppflow package on gcc
if: ${{ contains(matrix.cxx, 'g++-10') }}
run: |
conan create ./cppflow/ -pr:h=default -pr:b=default -s compiler.libcxx=libstdc++11
- name: Create cppflow package on apple-clang
if: ${{ contains(matrix.cxx, 'clang++') }}
run: |
conan create ./cppflow/ -pr:h=default -pr:b=default
- name: Conan install on gcc
if: ${{ contains(matrix.cxx, 'g++-10') }}
run: conan install ${{github.workspace}} -if ${{github.workspace}}/build -s compiler.libcxx=libstdc++11 --build missing -o mpi=${{matrix.mpi}} -o openmp=${{matrix.omp}} -o cppflow=on -pr:h=default -pr:b=default
Expand All @@ -143,7 +157,7 @@ jobs:
- name: Build
# Build your program with the given configuration.
# The Github Actions machines are dual-core so we can build faster using 2 parallel processes
run: conan build ${{github.workspace}} -bf ${{github.workspace}}/build
run: conan build ${{github.workspace}} -bf ${{github.workspace}}/build

- name: Test
working-directory: ${{github.workspace}}/build
Expand Down
16 changes: 13 additions & 3 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false

env:
CONAN_PRINT_RUN_COMMANDS: 1
CONAN_REVISIONS_ENABLED: 1
Expand All @@ -32,13 +32,13 @@ jobs:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}

# Note that we are installing doxygen with apt here, and with conan in the Configure step.
# This is because there is no easy way of extracting the path to the doxygen executable from conan.
# the apt installed doxygen is used in the Make Docweb step to build the html files,
# while the conan installed version is used in the build step.
# The version we install from apt is older than the 1.9.4 we install from conan, but
# it is not causing any issues at the moment.
# it is not causing any issues at the moment.
- name: Install dependencies
run: |
sudo apt update
Expand All @@ -56,6 +56,16 @@ jobs:
version: 2.11.0
os: linux

- name: Checkout cppflow repo
uses: actions/checkout@v3
with:
repository: UCL/cppflow.git
path: cppflow
ref: master

- name: Create cppflow package
run: conan create ./cppflow/ -pr:h=default -pr:b=default

- name: Configure
run: |
conan install doxygen/1.9.4@#2af713e135f12722e3536808017ba086 --update
Expand Down
55 changes: 0 additions & 55 deletions .github/workflows/macdebug.yml

This file was deleted.

3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "cppflow"]
path = cppflow
url = [email protected]:UCL/cppflow.git
[submodule "lexci_models"]
path = lexci_models
url = [email protected]:astro-informatics/lexci_models.git
77 changes: 47 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Dependencies installation
- [GCC](https://gcc.gnu.org) v7.3.0 GNU compiler for `C++`.
- [UCL/GreatCMakeCookOff](https://github.com/UCL/GreatCMakeCookOff) Collection of `CMake` recipes. Downloaded automatically if absent.
- [OpenMP](http://openmp.org/wp/) v4.8.4 (Trusty) - Optional - Speeds up some of the operations.
- [Cppflow](https://github.com/UCL/cppflow) - Optional - A warpper for the Tensorflow C API allowing us to read Tensorflow models into SOPT. Needed if you are using a learned prior.
- [Conan](https://docs.conan.io/en/latest/installation.html) - C++ package manager which installs the following:
- [Eigen3](http://eigen.tuxfamily.org/index.php?title=Main_Page) v3.2.0 (Trusty) Modern `C++` linear algebra. Downloaded automatically if absent.
- [spdlog](https://github.com/gabime/spdlog) v* - Optional - Logging library. Downloaded automatically if
Expand All @@ -42,45 +43,61 @@ Dependencies installation
Installing and building SOPT
----------------------------

You can build **SOPT** entirely from the source code. Once the mandatory dependencies are present, `git clone` from the [GitHub repository](https://github.com/astro-informatics/sopt):
You can build **SOPT** entirely from the source code.

``` bash
git clone https://github.com/astro-informatics/sopt.git
```

Then, the program can be built with standard `CMake` command:

``` bash
cd /path/to/code
mkdir build
cd build
conan install .. --build missing
conan build ..
```
1. If you are using a learned prior you must install the Tensorflow C API and `cppflow` package:
- Install [TensorFlow C API](https://www.tensorflow.org/install/lang_c)
- Clone the UCL fork of cppflow and create a conan package using

To install in directory `INSTALL_FOLDER`, add the following options to the conan build command:
``` bash
git clone [email protected]:UCL/cppflow.git
conan create ./cppflow/ -pr:h=default -pr:b=default
```
Note that conan requires you to specify the host (h) and the build (b) profiles on the command
line (`-pr:h=default -pr:b=default`), if you haven't defined them in your conan profile.
``` bash
conan build .. -bf INSTALL_FOLDER -if .
```
1. Once the mandatory dependencies are present, `git clone` from the [GitHub repository](https://github.com/astro-informatics/sopt):
CMake build options should be passed as options to `conan install` using the `-o` flag with a value `on` or `off`. Possible options are
``` bash
git clone https://github.com/astro-informatics/sopt.git
```
- tests (default on)
- benchmarks (default off)
- examples (default on)
- logging (default on)
- openmp (default on)
- mpi (default on)
- docs (default off)
- coverage (default off)
1. Then, the program can be built using conan:
For example, to build with both MPI and OpenMP off you would use
``` bash
cd /path/to/code
mkdir build
cd build
conan install .. --build missing -pr:h=default -pr:b=default
conan build ..
```
``` bash
conan install .. --build missing -o openmp=off -o mpi=off
conan build ..
```
- To install in directory `INSTALL_FOLDER`, add the following options to the conan build command:
``` bash
conan build .. -bf INSTALL_FOLDER -if .
```
- CMake build options should be passed as options to `conan install` using the `-o` flag with a value `on` or `off`. Possible options are
- tests (default on)
- benchmarks (default off)
- examples (default on)
- logging (default on)
- openmp (default on)
- mpi (default on)
- docs (default off)
- coverage (default off)
- cppflow (default off)
For example, to build with both MPI and OpenMP off you would use
``` bash
conan install .. --build missing -o openmp=off -o mpi=off -pr:h=default -pr:b=default
conan build ..
```
Common errors
-------
Expand Down
1 change: 1 addition & 0 deletions cmake_files/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ endif()
set(SOPT_MPI ${MPI_FOUND})

if(cppflow)
find_package(cppflow)
find_library(TENSORFLOW_LIB tensorflow REQUIRED)
endif()
3 changes: 3 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def requirements(self):
if self.options.logging == 'on':
self.requires("spdlog/1.9.2")

if self.options.cppflow == 'on':
self.requires("cppflow/2.0.0")

def build_requirements(self):

if self.options.docs == 'on':
Expand Down
2 changes: 1 addition & 1 deletion cpp/sopt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ if (cppflow)
# https://stackoverflow.com/questions/25676277/cmake-target-include-directories-prints-an-error-when-i-try-to-add-the-source
# Add /usr/local/include for default location of TensorFlow headers
target_include_directories(sopt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/cppflow/include>
$<BUILD_INTERFACE:${cppflow_INCLUDE_DIR}>
$<BUILD_INTERFACE:/usr/local/include>
$<INSTALL_INTERFACE:cppflow/include/>
)
Expand Down
53 changes: 50 additions & 3 deletions cpp/sopt/cppflow_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
#include <cppflow/cppflow.h>
#include "cppflow/ops.h"
#include "cppflow/model.h"
#include <stdexcept>

namespace sopt {
namespace cppflowutils {

// arbitrary constant for imaginary part of image vectors.
const double imaginary_threshold = 1e-10;

cppflow::tensor convert_image_to_tensor(Image<double> const &image, int image_rows, int image_cols){
// Convert the Sopt::Image of doubles(wrapper for Eigen::Array) to a cppflow::tensor of floats
// TODO: Make types template parameters
Expand All @@ -16,7 +21,7 @@ namespace cppflowutils {
std::vector<float> input_values(image_rows*image_cols, 1);
for (int i = 0; i < image.rows(); ++i) {
for (int j = 0; j < image.cols(); ++j) {
input_values[j*image_cols+i] = image(i,j);
input_values[j*image_cols+i] = image(i,j);
}
}

Expand All @@ -26,16 +31,58 @@ namespace cppflowutils {
return input_tensor;
}

cppflow::tensor convert_image_to_tensor(Image<std::complex<double>> const &image, int image_rows, int image_cols) {
// Convert the Sopt::Image of complex (wrapper for Eigen::Array) to a cppflow::tensor of floats
// Only takes the real part for processing, on the assumption that the imaginary part is negligible.
// If ratio of imaginary part to real part is greater than the threshold then this will throw an exception.
// TODO: Make types template parameters
// create a vector of the right shape (model expects extra dimensions on start and end)
std::vector<int64_t> input_shape = {1, image_rows, image_cols, 1};

std::vector<float> input_values(image_rows*image_cols, 1);

for (int i = 0; i < image.rows(); ++i) {
for (int j = 0; j < image.cols(); ++j) {
if(std::abs(image(i,j).real()) > cppflowutils::imaginary_threshold)
{
throw std::runtime_error("Cannot convert to tensorflow format: imaginary component of image is non-negligible.");
}
input_values[j*image_cols+i] = image(i,j).real();
}
}

// create a tensor from vector
cppflow::tensor input_tensor(input_values, input_shape);
return input_tensor;
}

// Convert an image stored in a sopt::Vector<double> to a cppflow::tensor of floats
cppflow::tensor convert_image_to_tensor(sopt::Vector<double> const &image, int image_rows, int image_cols) {

std::vector<float> values(&image[0], image.data()+image.size());
cppflow::tensor input_tensor(values, {1, image_rows, image_cols, 1});
std::vector<float> input_values(&image[0], image.data()+image.size());
cppflow::tensor input_tensor(input_values, {1, image_rows, image_cols, 1});

return input_tensor;

}

// Convert an image stored in a sopt::Vector<double> to a cppflow::tensor of floats
cppflow::tensor convert_image_to_tensor(sopt::Vector<std::complex<double>> const &image, int image_rows, int image_cols) {

std::vector<float> input_values(image_rows);
for(int i = 0; i < image_rows; i++)
{
if(std::abs(image(i).real()) > cppflowutils::imaginary_threshold)
{
throw std::runtime_error("Cannot conver to tensorflow format: imaginary component of image vector is non negligible.");
}
input_values[i] = image(i).real();
}
cppflow::tensor input_tensor(input_values, {1, image_rows, image_cols, 1});

return input_tensor;
}

// Convert model output in a std::vector into a sopt::Image (2D Eigen::Array)
Eigen::Map<Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic>> convert_tensor_to_image(std::vector<float> model_output, int image_rows, int image_cols){
// convert tensor of floats to Eigen::Array of doubles
Expand Down
4 changes: 4 additions & 0 deletions cpp/sopt/cppflow_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
#include "sopt/types.h"
#include <cppflow/cppflow.h>
#include "cppflow/ops.h"
#include <complex>

namespace sopt {
namespace cppflowutils {

//! Converts a sopt::Image to a cppflow::tensor
cppflow::tensor convert_image_to_tensor(sopt::Image<double> const &image, int image_rows, int image_cols);
cppflow::tensor convert_image_to_tensor(sopt::Image<std::complex<double>> const &image, int image_rows, int image_cols);
//! Converts a sopt::Vector to a cppflow::tensor
cppflow::tensor convert_image_to_tensor(sopt::Vector<double> const &image, int image_rows, int image_cols);
cppflow::tensor convert_image_to_tensor(sopt::Vector<std::complex<double>> const &image, int image_rows, int image_cols);

//! Convert a cppflow:tensor to an Eigen::Array
Eigen::Map<Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic>> convert_tensor_to_image(std::vector<float> model_output, int image_rows, int image_cols);
Expand Down
Loading

0 comments on commit 2031067

Please sign in to comment.