Skip to content

Commit

Permalink
Merge pull request #1 from matekelemen/feature
Browse files Browse the repository at this point in the history
Add support for symmetric matrices
  • Loading branch information
matekelemen authored Oct 30, 2023
2 parents d2dd1c4 + d24bb24 commit 2b563f0
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 191 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/*
!.gitignore
!ci.yml
!testrunner.yml
!packager.yml
44 changes: 44 additions & 0 deletions .github/workflows/packager.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: PKG

on:
push:
branches:
- master
paths-ignore:
- "*.md"
- "*.gitignore"
- "*.txt"
- "!CMakeLists.txt"
- "*.png"
- "*.mtx"
- "*.mm"
workflow_dispatch:

jobs:
packager:
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- name: Install toolchain
run: sudo apt install gcc-13 g++-13
- name: Clone repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build
run: |
./build.sh \
-t Release \
-o "-DCMAKE_CXX_COMPILER=/usr/bin/g++-13" \
-p
- name: Upload binary artifact
uses: actions/upload-artifact@v3
with:
name: release-ubuntu
path: build/*Linux.zip
- name: Upload source artifact
uses: actions/upload-artifact@v3
with:
name: release-source
path: build/*Source.zip
43 changes: 7 additions & 36 deletions .github/workflows/ci.yml → .github/workflows/testrunner.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
name: CI

on:
push:
pull_request:
branches:
- master
paths-ignore:
- "*.md"
- "*.gitignore"
- "*.txt"
- "!CMakeLists.txt"
- "*.png"
- "*.mtx"
- "*.mm"
workflow_dispatch:

concurrency:
Expand Down Expand Up @@ -42,39 +44,8 @@ jobs:
cat .github/assets/fidap005.mtx | build/bin/mtx2img - out.png
build/bin/mtx2img .github/assets/fidap005.mtx - > out.png
cat .github/assets/fidap005.mtx | build/bin/mtx2img - - > out.png
build/bin/mtx2img .github/assets/fidap005.mtx out.png -w 10
build/bin/mtx2img .github/assets/fidap005.mtx out.png -h 20
build/bin/mtx2img .github/assets/fidap005.mtx out.png -w 10 -h 20
build/bin/mtx2img .github/assets/fidap005.mtx out.png -w 10 -h 20 -c binary
build/bin/mtx2img .github/assets/fidap005.mtx out.png -w 10 -h 20 -c kindlmann
build/bin/mtx2img .github/assets/fidap005.mtx out.png -w 10 -h 20 -c viridis
build/bin/mtx2img .github/assets/fidap005.mtx out.png -r 10
build/bin/mtx2img .github/assets/fidap005.mtx out.png -r 10 -c binary
build/bin/mtx2img .github/assets/fidap005.mtx out.png -r 10 -c kindlmann
build/bin/mtx2img .github/assets/fidap005.mtx out.png -r 10 -c viridis
build/bin/mtx2img --help
package:
needs: testrunner
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- name: Install toolchain
run: sudo apt install gcc-13 g++-13
- name: Clone repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build
run: |
./build.sh \
-t Release \
-o "-DCMAKE_CXX_COMPILER=/usr/bin/g++-13" \
-p
- name: Upload binary artifact
uses: actions/upload-artifact@v3
with:
name: release-ubuntu
path: build/*Linux.zip
- name: Upload source artifact
uses: actions/upload-artifact@v3
with:
name: release-source
path: build/*Source.zip
8 changes: 4 additions & 4 deletions include/mtx2img/mtx2img.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ enum class Data
{
Real, // <== supported
Integer, // <== supported
Complex, // <== unsupported, planned
Complex, // <== supported
Pattern // <== supported
}; // enum class Data


enum class Structure
{
General, // <== supported
Symmetric, // <== unsupported, planned
SkewSymmetric, // <== unsupported, planned
Hermitian // <== unsupported, planned
Symmetric, // <== supported
SkewSymmetric, // <== supported
Hermitian // <== supported
}; // enum class Structure


Expand Down
5 changes: 2 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ Convert [sparse matrix market](https://math.nist.gov/MatrixMarket/formats.html#M
<img src=".github/assets/cube_isoparametric_quadratic_tets.png" width=300/> <img src=".github/assets/rbs480a.png" width=300/>
</p>

`mtx2img <input-path> <output-path> [-w <output-image-width>] [-h <output-image-height>] [-c <colormap-name>]`
`mtx2img <input-path> <output-path> [-r <output-resolution>] [-c <colormap-name>]`

Required arguments:
- `<input-path>`: path pointing to an existing MatrixMarket file (*.mtx* or *.mm*). It must use the *coordinate* format (i.e.: represent a sparse matrix). Alternatively, `-` can be passed to read the same format from *stdin* instead of a file.
- `<output-path>`: the output image will be written here. If a file already exists, it will be overwritten. If the path exists but is not a file, the program will fail without touching the output path. Alternatively, `-` can be passed to write the output image to *stdout*.

Optional arguments:
- `[-w <output-image-width>]`: width of the output image in pixels. This setting is overridden if the number of rows in the input matrix is less than the provided width. The default value is 1080.
- `[-h <output-image-height>]`: height of the output image in pixels. This setting is overridden if the number of columns in the input matrix is less than the provided height. The default value is 1080.
- `[-r <output-resolution>]`: highest resolution of the output image in pixels. This setting is overridden if the number of rows or columns in the input matrix is less than the provided width, but the its aspect ratio as always preserved (closest ratio representable by the requested resolution). The default value is 1080.
- `[-c <colormap-name>]`: name of the colormap to apply on pixels. The options are limited to `binary` (default) or `kindlmann`. If the matrix dimensions are larger than the output image dimensions, multiple nonzeros may end up getting mapped to the same pixel. The program sums up the number of nonzeros that reference each pixel, and normalizes this "*nonzero density*" after reading the matrix. The `-c` option controls how these densities are mapped to RGB colors in the output image.
- `binary`: any pixel with at least one nonzero mapping to it is black; the rest are white.
- [`kindlmann`](https://www.kennethmoreland.com/color-advice/#extended-kindlmann) (extended)
Expand Down
73 changes: 28 additions & 45 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
* - binary colormap (any pixel with a nonzero is black, rest are white)
*/
const std::map<std::string,std::string> defaultArguments {
{"-w", "1080"},
{"-h", "1080"},
{"-r", "1080"},
{"-c", "binary"}
};

Expand All @@ -32,8 +31,7 @@ struct Arguments
{
std::filesystem::path inputPath;
std::filesystem::path outputPath;
std::size_t width;
std::size_t height;
std::size_t resolution;
std::string colormap;
}; // struct Arguments

Expand All @@ -44,13 +42,12 @@ void printHelp()
<< "Help: convert large sparse matrices from MatrixMarket format to images.\n"
<< "Usage: mtx2img <path-to-source> <path-to-output> [OPTION ARGUMENT] ...\n"
<< "Options:\n"
<< " -w <width> : width of the output image in pixels (default: " << defaultArguments.at("-w") << ").\n"
<< " -h <height> : height of the output image in pixels (default: " << defaultArguments.at("-h") << ").\n"
<< " -c <colormap> : colormap to use for per pixel nonzero density. Options: [binary, kindlmann, viridis] (default: " << defaultArguments.at("-c") << ").\n"
<< " -r <resolution> : highest resolution of the output image in pixels (default: " << defaultArguments.at("-r") << ").\n"
<< " -c <colormap> : colormap to use for per pixel nonzero density. Options: [binary, kindlmann, viridis] (default: " << defaultArguments.at("-c") << ").\n"
<< "\n"
<< "The input path must point to an existing MatrixMarket file (or pass '-' to read the same format from stdin).\n"
<< "The parent directory of the output path must exist, and the output path is assumed to either not exist, or\n"
<< "point to an existing file (in which case it will be overwritten)."
<< "point to an existing file (in which case it will be overwritten).\n"
;
}

Expand Down Expand Up @@ -192,42 +189,23 @@ std::optional<Arguments> parseArguments(int argc, char const* const* argv)
}
}

// Convert and validate width
// Convert and validate resolution
char* it_end = nullptr;
std::string& r_widthString = argMap["-w"];
const long long width = std::strtoll(r_widthString.data(), &it_end, 0);
if (it_end < r_widthString.data() ||
static_cast<std::size_t>(std::distance(r_widthString.data(), it_end)) != r_widthString.size()) {
const std::string& r_resolutionString = argMap["-r"];
const long long resolution = std::strtoll(r_resolutionString.data(), &it_end, 0);
if (it_end < r_resolutionString.data() ||
static_cast<std::size_t>(std::distance(r_resolutionString.data(), static_cast<const char*>(it_end))) != r_resolutionString.size()) {
throw std::invalid_argument(std::format(
"Error: invalid output image width: {}\n",
r_widthString
"Error: invalid output image resolution: {}\n",
r_resolutionString
));
} else if (width < 0) {
} else if (resolution < 0) {
throw std::invalid_argument(std::format(
"Error: negative output image width: {}\n",
r_widthString
"Error: negative output image resolution: {}\n",
r_resolutionString
));
} else {
arguments.width = static_cast<std::size_t>(width);
}

// Convert and validate height
it_end = nullptr;
std::string& r_heightString = argMap["-h"];
const long long height = std::strtoll(r_heightString.data(), &it_end, 0);
if (it_end < r_heightString.data() ||
static_cast<std::size_t>(std::distance(r_heightString.data(), it_end)) != r_heightString.size()) {
throw std::invalid_argument(std::format(
"Error: invalid output image height: {}\n",
r_heightString
));
} else if (height < 0) {
throw std::invalid_argument(std::format(
"Error: negative output image height: {}\n",
r_heightString
));
} else {
arguments.height = static_cast<std::size_t>(height);
arguments.resolution = static_cast<std::size_t>(resolution);
}

return arguments;
Expand Down Expand Up @@ -288,7 +266,7 @@ int main(int argc, char const* const* argv)
}

// Set up output stream
std::string outputPath = arguments.outputPath.string();
const std::string outputPath = arguments.outputPath.string();
std::ostream* p_outputStream = nullptr;
std::optional<std::ofstream> maybeOutputFile;

Expand All @@ -302,6 +280,11 @@ int main(int argc, char const* const* argv)
}

std::vector<unsigned char> image;
std::pair<
std::size_t, // <== width
std::size_t // <== height
> imageSize {arguments.resolution, arguments.resolution};

#ifdef NDEBUG
try {
#endif
Expand All @@ -310,8 +293,8 @@ int main(int argc, char const* const* argv)
// Note: the image gets resized if the matrix dimensions
// are smaller than the requested image dimensions.
image = mtx2img::convert(*p_inputStream,
arguments.width,
arguments.height,
imageSize.first,
imageSize.second,
arguments.colormap);

#ifdef NDEBUG
Expand All @@ -333,11 +316,11 @@ int main(int argc, char const* const* argv)
if (stbi_write_png_to_func(
writeImageData, // <== write functor
reinterpret_cast<void*>(p_outputStream), // <== write context (output stream)
arguments.width, // <== image width
arguments.height, // <== image height
image.size() / arguments.width / arguments.height, // <== number of color channels
imageSize.first, // <== image width
imageSize.second, // <== image height
image.size() / imageSize.first / imageSize.second, // <== number of color channels
static_cast<const void*>(image.data()), // <== pointer to image data
image.size() / arguments.height * sizeof(decltype(image)::value_type) // <== bytes per row
image.size() / imageSize.second * sizeof(decltype(image)::value_type) // <== bytes per row
) == 0) {
std::cerr << "Error: failed to write output image.\n";
return 1;
Expand Down
Loading

0 comments on commit 2b563f0

Please sign in to comment.