Skip to content

Commit b9c03e7

Browse files
authored
Merge pull request #44 from jalvesz/dev
feat : add spy functionality
2 parents 270e32d + d08832c commit b9c03e7

5 files changed

Lines changed: 154 additions & 2 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ call suptitle("figure title")
124124
- `table` for a block of text below (or above) the axes
125125
- `streamplot` for the streamlines of a vector field
126126
- `add_frame` / `save_animation(file, fps=, loop=)` to write an animated GIF
127-
- `matshow`, `eventplot` and `broken_barh`
127+
- `spy` (sparsity pattern of a dense matrix or COO indices), `matshow`, `eventplot` and `broken_barh`
128128
- `hist2d` and `hexbin` for counting points into square or hexagonal bins
129129
- `imshow(interpolation="bilinear")` to smooth an image instead of
130130
showing it as blocks

‎docs/gallery/plot_spy.f90‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
! Spy: sparsity patterns
2+
! ======================
3+
!
4+
! The nonzero structure of a matrix, as matplotlib's
5+
! `spy <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.spy.html>`_
6+
! draws it: one square marker per stored entry, row 1 at the top and the
7+
! column numbers along the top edge.
8+
!
9+
! :f:subr:`spy` takes either a dense matrix, whose entries above ``precision``
10+
! in magnitude are marked, or the COO indices of a sparse one as a
11+
! ``(2, nnz)`` integer array of rows and columns. Indices start at 1.
12+
13+
program plot_spy
14+
use fplotlib
15+
implicit none
16+
17+
integer, parameter :: n = 30
18+
real(dp) :: a(n, n)
19+
integer, allocatable :: ij(:, :)
20+
integer :: i, k
21+
22+
! %%
23+
! A dense matrix: the tridiagonal of a 1D Laplacian, plus two corner entries
24+
! and an anti-diagonal of tiny values that ``precision`` filters out.
25+
26+
a = 0.0_dp
27+
do i = 1, n
28+
a(i, i) = 2.0_dp
29+
if (i > 1) a(i, i - 1) = -1.0_dp
30+
if (i < n) a(i, i + 1) = -1.0_dp
31+
a(i, n + 1 - i) = a(i, n + 1 - i) + 1.0e-9_dp
32+
end do
33+
a(1, n) = -1.0_dp
34+
a(n, 1) = -1.0_dp
35+
36+
call figure(figsize=[10.0_dp, 5.0_dp])
37+
call subplot(1, 2, 1)
38+
call spy(a, precision=1.0e-6_dp, markersize=4.0_dp)
39+
call title("dense, precision = 1e-6")
40+
41+
! %%
42+
! A sparse matrix given only by its COO indices: a 5-point stencil on a
43+
! 6 x 6 grid. The shape ``m``, ``n`` is passed so that empty trailing rows or
44+
! columns still show.
45+
46+
k = 0
47+
allocate (ij(2, 5 * 36))
48+
block
49+
integer :: gx, gy, r
50+
do gy = 1, 6
51+
do gx = 1, 6
52+
r = (gy - 1) * 6 + gx
53+
k = k + 1; ij(:, k) = [r, r]
54+
if (gx > 1) then; k = k + 1; ij(:, k) = [r, r - 1]; end if
55+
if (gx < 6) then; k = k + 1; ij(:, k) = [r, r + 1]; end if
56+
if (gy > 1) then; k = k + 1; ij(:, k) = [r, r - 6]; end if
57+
if (gy < 6) then; k = k + 1; ij(:, k) = [r, r + 6]; end if
58+
end do
59+
end do
60+
end block
61+
62+
call subplot(1, 2, 2)
63+
call spy(ij(:, 1:k), m=36, n=36, markersize=5.0_dp, color="C1")
64+
call title("COO indices")
65+
66+
call savefig("spy.svg")
67+
68+
print *, "wrote spy.svg"
69+
70+
end program plot_spy

‎src/fplotlib.f90‎

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module fplotlib
2929
public :: bar, barh, hist, fill_between, fill_betweenx, stackplot
3030
public :: errorbar, axhline, axvline, axline
3131
public :: pcolormesh, pcolor, hist2d, hexbin
32-
public :: matshow, eventplot, broken_barh, streamplot, table
32+
public :: spy, matshow, eventplot, broken_barh, streamplot, table
3333
public :: add_axes, secondary_xaxis, secondary_yaxis
3434
public :: add_rectangle, add_circle, add_ellipse, add_polygon
3535
public :: add_arrow, add_path
@@ -197,6 +197,11 @@ module fplotlib
197197
module procedure imshow_z, imshow_rgb
198198
end interface imshow
199199

200+
! Sparsity pattern of a dense matrix, or of a COO index array.
201+
interface spy
202+
module procedure spy_dense, spy_coo
203+
end interface spy
204+
200205
! One dataset or a whole block of them, a row to each.
201206
interface boxplot
202207
module procedure boxplot_one, boxplot_many
@@ -2704,6 +2709,73 @@ subroutine imshow_rgb(z, extent, origin, aspect)
27042709
end if
27052710
end subroutine imshow_rgb
27062711

2712+
! matplotlib's spy for a dense matrix: a marker at every entry whose
2713+
! magnitude exceeds precision (default 0, i.e. every non-zero entry).
2714+
! Indices are Fortran's, so the first row and column sit at 1.
2715+
subroutine spy_dense(a, precision, markersize, color, marker)
2716+
real(dp), intent(in) :: a(:, :)
2717+
real(dp), intent(in), optional :: precision, markersize
2718+
character(len=*), intent(in), optional :: color, marker
2719+
real(dp) :: tol
2720+
logical, allocatable :: mask(:, :)
2721+
integer, allocatable :: ij(:, :)
2722+
integer :: i, j, k
2723+
2724+
tol = 0.0_dp
2725+
if (present(precision)) tol = abs(precision)
2726+
allocate (mask(size(a, 1), size(a, 2)))
2727+
mask = abs(a) > tol
2728+
allocate (ij(2, count(mask)))
2729+
k = 0
2730+
do j = 1, size(a, 2)
2731+
do i = 1, size(a, 1)
2732+
if (mask(i, j)) then
2733+
k = k + 1
2734+
ij(:, k) = [i, j]
2735+
end if
2736+
end do
2737+
end do
2738+
call spy_coo(ij, size(a, 1), size(a, 2), markersize, color, marker)
2739+
end subroutine spy_dense
2740+
2741+
! Sparsity pattern from COO indices: ij(1, :) are the rows and ij(2, :)
2742+
! the columns of the stored entries, 1-based. m and n are the matrix
2743+
! shape; without them the largest index in each is used.
2744+
subroutine spy_coo(ij, m, n, markersize, color, marker)
2745+
integer, intent(in) :: ij(:, :)
2746+
integer, intent(in), optional :: m, n
2747+
real(dp), intent(in), optional :: markersize
2748+
character(len=*), intent(in), optional :: color, marker
2749+
integer :: nr, nc
2750+
real(dp) :: ms
2751+
character(len=:), allocatable :: mk
2752+
2753+
if (size(ij, 1) /= 2) error stop "fplotlib: spy needs a (2, nnz) index array"
2754+
nr = 0
2755+
nc = 0
2756+
if (size(ij, 2) > 0) then
2757+
nr = maxval(ij(1, :))
2758+
nc = maxval(ij(2, :))
2759+
end if
2760+
if (present(m)) nr = m
2761+
if (present(n)) nc = n
2762+
ms = 10.0_dp
2763+
if (present(markersize)) ms = markersize
2764+
mk = "s"
2765+
if (present(marker)) mk = marker
2766+
2767+
call ensure_fig()
2768+
if (size(ij, 2) > 0) then
2769+
call plot(real(ij(2, :), dp), real(ij(1, :), dp), marker=mk, &
2770+
linestyle="None", color=color, markersize=ms)
2771+
end if
2772+
call xlim(0.5_dp, real(nc, dp) + 0.5_dp)
2773+
call ylim(0.5_dp, real(nr, dp) + 0.5_dp)
2774+
ax(cur_i)%y_inv = .true.
2775+
ax(cur_i)%x_top = .true.
2776+
call set_aspect(1.0_dp, "box")
2777+
end subroutine spy_coo
2778+
27072779
! matshow: an image of a matrix. The first row is at the top, the
27082780
! cells are square and the column numbers run along the top, which is
27092781
! how a matrix is written down.

‎tests/fingerprints.txt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ interp 640 480 00000000000000000000000000000000000000000000000000000000000000000
7070
hist2d 640 480 4041a024a1408408b2be65f4e8f4b0b67cc8f82c0babc84d0a4645ca7e988f58d994304b3d33dd453f6d8d6b6238624271ca4f372bea702e58ba63044e5861147da1e388fb32744ed88ee843cb9b8d0814252e2df217d18c501c62262e5d5ad05f416b56220b7207a821074c2f6aba9df04383d12a242f9a9b83430147f196987de7a1a00bf1a60930384d27aac7b6df24695566e48f9d9b2d83614c228de1ab22659fd578d296507370313c70f61555802ca0470b50440ceec919ae3c78eef65bc7f5e6aefef5aab1b2ab8d18181498c5e13f856b05c04010d5ffb3dbf44e40664862ef15129c037c03286631713065b3b19704034a2ba0b1e31e8e59ee8e56d38bc2e9955e3222a66adfd2aa13d5499910dafc0656a15c85605cbdd2275e0e3ac380fd56e3a4e7db600f67944500136414f24bc8fa63e2b9d4196a4e322bfa587d62c2e7045523f87c6359f549420f1c993003086110082205844ab7e174200338bae7e7e6781af66afa8f2407a262ec71a966c006340ecd31ec6675cde9b8ac5f9aade059bce0120da2b02dfcdb4b3d1bdc07a246726217b2d9f0905c1f0c487325a60c42d53621f43d9e37790b3800c2c400085444c297ad71b432368ba26b85999e05070eca26974bfbab957b1168d2d2be79480d0ede644eec8b5ba0752503aa246d065c66c226f84a8d450ce97165589ad149a2faf54500846f3ab3bf
7171
hexbin 640 480 99b30fc0fb6c26fa6b0ac75f8750ad84afd225facaa312911eae6b8e1147bd12206262f1348f73b5f3324344e81fe043e70a0791528a781c16491874ad5264974252b65e2bb0996ad9f163d966983b88d362951b2c3b99fd19ac23ee173b96e393f1285ccf1b665691044f9cae5d2a9e066b34d07a21261bb22a09259113103596a27af462140464114d33ad11e8a2cfd49c7d0072aa2c00f65b094481536e3dcb41a46a6d4c0872a24617b1c470493fd19fcae2177fa3e236083cca6c5322db4b3281d2772ea03c5d15b6f9b214345245f0804dbf253f67d2a6ebd526f4d888c13e6d251c575b7f382008792235aa8921b9c45258d9ddf15f21dcd4ab574f298e182f9d82c67b86eebbe29e95712ffe77bd969572f30511628e61de7304024f24636db949feae604e5a7c266b4ba168a0bb22a22f00dac7d105331fa322f1dd2719d31d35c8813cdbb39a41c712f74a521cf639bbbbf10cd7c79b6cdcebd0e12c8936af783982b94d5247703532995e7b0158ed6b183df348348f4e638f505df5284f4ab34d16c3291f9cab23b5c1e59d77105e3adc14d2377cc382688cc91f0d1cfd2396476abc90d03752632f9ab06067bb1d5653154fabc718e463a94a44de07b388248af882012efdc8c3032dc88a14d2ee9613da8831ecd19ee3a6f9e66f787f1bf34dabc6274e8199c9dc08e90116a552f3024a0d3d394440f1e6099d
7272
matshow 640 480 0000000000000000c1f68b49611d1876986106d3089f6b02e98149e9b46f3298085c49f68f7eed97b10e4210dc621374fdbb9977ee00e71000000000000000000000000000000000d8c64c7df3935f7abf242648a0005daf8e9ea78807bd9d6eff4d1a76a081f49ef40077f2b384f49fdc134541f8b4f55c000000000000000000000000000000006c8a4986bed62f6d0863022214a004009500c1010059581606d404c4cde74009a16a024224a4263a8d50298c65561a650000000000000000000000000000000052335479d4708a8228000236048125042020431142020002408a002034284242412010608680800181b07cdde6fd70b00000000000000000000000000000000001aa17bd55a6165408408b842c8008020129220010040080018001231d3a900008284a44a5d489009a173264033709bf000000000000000000000000000000007ec1071f89d2708540000091ab8c100f405240520000100825c8c022a0207a0acab2bfa439931cc4431f41589f1e64a0000000000000000000000000000000001b47500a4108cef61891232a943593221840809904d4481048ce4b7f0fe405918b537d8a464b63783d83e18e91c8533d000000000000000000000000000000006a2e4ca12666f2f5e8276fccb099ad081b64144b04af8dce600ffea7b95202b34e4d9e0bf908c28fe21eaaa0783e9c740000000000000000
73+
spy_dense 640 480 070b551691b94f3dd058af2c1396cff796b670ab97129db4649308f79ce832988b7a46291f6ea7058c290af8ef56ae935e72fc48d082b86765ae61ad337599a5c49bc08167376fccccc40107209da90588005c700c4e17b827098d900c6b41d901452a4208db47295276f945b304fdd25293a1ea828285c3baf53bfd16dcefc17101407e64101804dc5a2e30c74e3ae22f87f52aca141c1e0000000000000000000000000000000000000000000000000000000000000000d1b326e7f4fcbf8f7ce262184e01cd1db7495b2512e113a700000000000000000000000000000000000000000000000000000000000000000000000000000000a8dcd1a316ed0287778b03054cdb512131b886bc8666833c00000000000000000000000000000000000000000000000000000000000000000000000000000000bb6a6bd70a94057d2da5868b1cb92291cb38c34b6d7e65400000000000000000c21b82c6550d0aa60e8cd3ddcb124e1900000000000000001c533293527ad8b8f1449c6d30d95b5e3370b23b73aade2c79157ebec5002952378ecba9879ce568caf8653c03deff98ba7cc37721ac02c54f456f98ed0ca7e91061cfcf4eaf535c228f2e88c0ce736a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
74+
spy_coo 640 480 000000000000000000000000000000000465ec06debc7efd121055f6285fe6282c8ba4012a049b7009b7770101b8eaaf02009400002020000000000000000000000000000000000000400014200040008d864e267378e4fc25db74127621f3f00b835ee019a572f5ec2609428b1e249a10827c018fac3ca20000000000000000000000000000000080004000282409001a4fba7e89bcbee700000000000000009a3395d70483c498ffff51ee1d2c36161951008767273794000000000000000000000000000000000000040100081088285b1e4a587f3b8b00000000000000000000000080000000c2d783c4c2086560e8702e96a521625b000000000000000000000000000000008010000011000000315e7006ceeb4ec600000000000000000000000000000000bdff9e7e67a29e2c3a10032dc0b41410000000000000000000000000000000000000214000020034296fdb84f1b7c3c7547615404964ffdb0000000000000000337260e050081e93032a47330c51e800000000000000000000000000000000002c202200028022009e68a62c80d4b49f00000000000000000000000000000000a3e6148089d8e04e444b546488826b6000000000000000000000000000000000000000000000000058793d7de4bc834fc42d084a25d3befea24e392a9727b215ea7e950e4aa003f1d01010800c2100000000000000000000
7375
eventplot 640 480 4001a02401400000ed1952e619b0e9b0a0c37cfae9f0cb73257cf3d338ba82b5ba1d1c2f1d583fb931f68bf7b6b9c555b3f93ad9bb4292d516491874ad52649751701daaa5a3c9f8ad3cf7db776fd496729cf567f2db6b4825db74127621f3f0ab835ae019a572d578176ae5641fcee21b80061009890390a359466920cac7cda7764185f5ae3a4835bde8a3578e7c2cfd84f44db00ae93b15b341ab4c16e85612829d37f23653dd86e700fa95b6b1459e0701520b3845f8d1b326e7f4fcbf8f85d522cf984a473cea6141ec322a8b2c19429fda9257ff2c0cbaf04fe9e66523c7c67d5b6dc4860c2b6987816f2262a819dff2fbe5950404a8dcd1a316ed0287550c77f6fae7c39f1d0397a1d804f77b6b17e358a38a05019df9d03c3fa5f8680339923f8f0c6735b8a82dcc7851ac790d3ab560d3782963bb6a6bd70a94057d529a8dcdcd842ced4f014962ee6e95bafd3e09a482ccd546cc347c5bcbff013f89b15e31c243608b79870f86b125f35ed70c520ac7017c0ef1449c6d30d95b5ed01a248c444ab0e7e8b9c871ae993ac2000000000000000000000000000000000000000000000000000000000000000000000000000000004cb261193db438d30201b01800000a416215fe4ee35fd93c81d163a5bff7c197ece1d342dc07c9cda97770045fd29a86abf30555ba2a9fcb22f5fe654c3218d46fc51c434a25072e
7476
broken_barh 640 480 4001a02401400000ed1952e619b0e9b0a0c37cfae9f0cb73a3d27805fa4a833db82411efb921440331f68bf7b6b9c555b3f93ad9bb4292d516491874ad5264971090d532cf35236f076fcba7022744f0f8e55dcfb707664773d1556cf8aa32ab23fab2f08a79781a4a22cfee60873198e4e13cd7e4612024a359466920cac7cd7602d92db3ed0dc3f9951315b0b243c20000000200200400f3b0bf28b1ebfc84b5095a1b3626aea80100110210100010bc831c3e55df4a01d1b326e7f4fcbf8f2a36094e0318a533705a48cbbd01a057ca52520950b9366e7513b421082a4877fc6551605a0aa87dfc9e327a5cb9751b5dd05298fc5fc82ba8dcd1a316ed0287c9ffc8d8f9b39b158f0174fd361d627c5ab98eb1be894e5b9cf5510699e06f7fced23ed2af2b8466a624c1dfe739f5b066f2783677d884d5bb6a6bd70a94057def0334f6ab4b7a7e156b3898b1d244e9e85ce835e763e8b12a63cd8933e2272f7e1040b0250ae2dc246c37ac1119616f10aadb6288b1071cf1449c6d30d95b5eaba825cbdab28af0b6e549c4b9e291d49ec8195458d4c2a3d443919fcb1c042c045d2b8b338117aa6a3a8bc6ef964428fc0207fd63247ba14cb261193db438d30201b00800000241de6024a4347b3cb65195cfa43f2c0de4a1cdcf4ee9f86e884194f737133831c970d2cf7a6713b2c1e53691fc93d134a5398796d272358e67
7577
streamplot 640 480 8b511cf775db026cfd11d0d69991f3b2a4c17dfa68f0c353dcdea2f1a007391f2deed5f97b9652023076c7b7eab0c54562b22bf6b83790d416491874ad526497947ab6eb1494826d0dd76b26db659273c53668666d8706cd1f7ed99cb383f61317b28ded290ff913b358e4c129ea51b5141af6e4d373472f04df34b829c01bdaeb63816b52123a2b338e126c578a3b4f961ea413140907a30ea74f8cd36042602e548c435e17818b85bfb79b5c884efab52223bfdf5aa7f2b032f6788e5e4b71468f74d88fae5ce3c304f74e6570b863de63ff9330bde0ac810eb0f326a3d93646572a49dc56879f1bd0cc9a0e6bba3ff16953c1e5eef569a3379df45f9614ce49b0b18393a8cbd6c5105f7e6dfca1e2cc32924711df44aeecef58f6e3e8c135b94d507a5488f4427958f5c0ebef939b0c62408392622570a6cadd0f6c60eb25f1693d9cbd1dc89ac68f5b5957c2cdbeb9b7b86a370b2faa029b80ea77ab93be7fcca7ae41a9a1b24288a989247d3d9ba7aaf42a9a743325b743f0dc96df23f10ca6dd07162e2d8dfc78d2e51df226b059bea507431f18be86fbea273e91a7a0809150dd5b3a353a3f7272664cb8060d568a099c33e1bd35c6b728daf07be7c1efdba1ebee3180aad35cbccdb1df16d18bfea8aa31045b9bac600343c926888677cde16c33a203c8413ab29cc5004aea212b2bd526a1114fe9ac0dfc894404e5

‎tests/test_plots.f90‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,14 @@ program test_plots
837837
call matshow(mz)
838838
call save_all("matshow")
839839

840+
call figure()
841+
call spy(reshape([1.0_dp, 0.0_dp, 0.0_dp, 2.0_dp, 0.0_dp, 3.0_dp], [2, 3]))
842+
call save_all("spy_dense")
843+
844+
call figure()
845+
call spy(reshape([1, 1, 2, 3, 4, 2], [2, 3]), m=5, n=4)
846+
call save_all("spy_coo")
847+
840848
! 70) a row of events
841849
call clf()
842850
do i = 1, 40

0 commit comments

Comments
 (0)