Add mutual information calculation utility - #6769
vk3089790-arch wants to merge 11 commits into
Conversation
* Implements `rz_mutual_info_init`, `rz_mutual_info_update`, and `rz_mutual_info_final` in `librz/util/mutual_info.c` * Computes mutual information between two byte buffers using joint and marginal probability distributions * Includes unit tests covering perfectly correlated and independent data cases
|
@wargio Addressed all the review comments — added a zero-length guard in |
wargio
left a comment
There was a problem hiding this comment.
why the pic and the code is not used anywhere. also missing the spdx headers.
29a6372 to
1cc6156
Compare
|
@wargio Removed the stray profile photo commit that got included by accident, and added SPDX headers to all three files (mutual_info.c, rz_mutual_info.h, test_mutual_info.c). Pushed in the latest commits. |
Rot127
left a comment
There was a problem hiding this comment.
The code is still unused. It needs to be put in action to be merged.
|
Good point, understood — I'll wire this into a real command so it's actually used. Would you prefer this exposed via |
|
i do not think this should go under rz-hash for any sort of reason. This is purely a change needed in rz-diff |
|
Got it, thanks — I'll add this as a new mode/flag in rz-diff. Will look at how existing modes are structured there and follow the same pattern. Will push once it's working. |
|
Added a new rz-diff -t mutualinfo README.md README.md → 5.268306 bits (identical) Pushed in the latest commit. |
…n tests Per review feedback, mutual information calculation now lives under the -d (distance) option family as 'mutualinfo', alongside myers, leven, lcs-roll, and ssdeep, rather than as a separate -t diff type. Since MI is measured in bits rather than a 0-1 similarity ratio, output uses a dedicated label instead of reusing 'similarity'. Added regression tests covering standard/JSON/quiet output modes.
|
Moved mutual information into the -d distance path as -d mutualinfo (removed the old -t mutualinfo mode), and added regression tests in test/db/tools/rz_diff covering standard/JSON/quiet output. Since MI is reported in bits rather than a 0–1 similarity ratio like the other algorithms, I gave it its own output label ("bits" in JSON) instead of reusing similarity — happy to change that if you'd prefer a different format. |
|
@Rot127 This should also address your earlier comment — the API is now wired into |
| printf("similarity: %.3f\n", similarity); | ||
| if (ctx->distance != DIFF_DISTANCE_SSDEEP) { | ||
| if (ctx->distance == DIFF_DISTANCE_MUTUALINFO) { | ||
| printf("mutual information: %.6f bits\n", similarity); |
Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>
There was a problem hiding this comment.
i have checked the algo, i think we are missing some info.
we should report the bits, the coverage (i.e. 100.0 * min(lenA, lenB) / max(lenA, lenB) ) & overlap size (i.e. min(lenA, lenB))
Because the amount of info lost is kinda important, example:
| lenA | lenB | coverage | overlap |
|---|---|---|---|
| 800 | 1000 | 80% | 800 bytes |
| 800000 | 1000000 | 80% | 800000 bytes |
There is a clear difference of info lost between 200000 & 200
Per wargio's follow-up, mutual information distance now also reports coverage (percentage of the larger file's bytes covered by the comparison) and overlap (the actual number of bytes compared, i.e. min(a_size, b_size)), in standard, JSON, and quiet output modes. Also reduced bits precision from 6 to 3 decimals in standard/quiet output for readability, consistent with the other distance algorithms. Updated regression tests to match.
|
@wargio I’ve fixed the issues you pointed out and pushed the changes to the add-mutual-info branch. I’ve also rebased the branch with the latest changes. Could you please take another look when you get a chance? Thanks! |
wargio
left a comment
There was a problem hiding this comment.
I can merge it once the last change is applied.
Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>
rz_mutual_info_init,rz_mutual_info_update, andrz_mutual_info_finalinlibrz/util/mutual_info.cYour checklist for this pull request
RZ_APIfunction and struct this PR changes.RZ_API).formula and to review my code for bugs (e.g. correct indexing when
computing marginal probabilities). I wrote the actual implementation
myself, filling in the core logic in rz_mutual_info_update and
rz_mutual_info_final line by line, and verified it compiles and passes
the included unit tests.
Detailed description
This PR implements a mutual information calculation utility for
librz/util,as requested in #5176.
What is mutual information?
Mutual information measures how much knowing the value of one variable
tells you about another — in this case, how correlated two byte buffers
are. It's useful for reverse engineering tasks like detecting related
fields in a file format, dependency between structure members, or
patterns in obfuscated data.
How it works
The implementation follows the same context-based pattern already used by
rz_entropy_*inlibrz/hash/algorithms/entropy/:rz_mutual_info_init— initializes a context holding a 256x256 jointfrequency table.
rz_mutual_info_update— walks two equal-length byte buffers inlockstep, incrementing the joint count for each observed byte pair.
Can be called multiple times to process data incrementally.
rz_mutual_info_final— derives marginal probabilities from the jointtable, then computes:
I(X;Y) = sum p(x,y) * log2(p(x,y) / (p(x) * p(y)))
over all non-zero joint probabilities.
Placement
This currently lives in
librz/util/mutual_info.cas a general-purposeutility, since it isn't tied to any specific tool. I'm following up with
maintainers on whether this should instead live alongside
rz-difforrz-hash, and will move it if so....
Test plan
Ran locally:
ninja -C build
./build/test/unit/test_mutual_info
Both test_mutual_info_basic and test_mutual_info_independent pass.
...
Closing issues
Part of #5176
...