Skip to content
This repository was archived by the owner on Aug 23, 2020. It is now read-only.

Feature: Improved CW Calculation #1451

Merged
merged 22 commits into from
Jul 31, 2019
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -2,12 +2,12 @@

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import java.util.Set;

import com.iota.iri.controllers.ApproveeViewModel;
import com.iota.iri.controllers.TransactionViewModel;
@@ -21,7 +21,10 @@
import com.iota.iri.utils.collections.interfaces.UnIterableMap;

/**
* Calculates the weight recursively/on the fly instead of building the tree and calculating after
* Calculates the weight recursively/on the fly
* Used to create a weighted random walks.
*
* @see <a href="cumulative.md">https://github.com/alongalky/iota-docs/blob/master/cumulative.md</a>
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give a better description of the algorithm?
Try not to refer to the other implementation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe delete the other implementation, then you can call this one Cumulative Weight Calculator. Then we won't have to maintain more code. Also it will make you improve the javadoc (you can define CW in the doc)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should have copied the description but not the Link!!
The link explains the old algo which you deleted

Simply write exactly the following:
"Calculates the cumulative weight for each transaction above a certain entry point. Cumulative weight is an integer defined as the number of approving transactions plus one. We calculate it by doing a graph traversal from each transaction above a certain entry point and counting the number of different transactions we pass."

public class RecursiveWeightCalculator implements RatingCalculator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change name to CumulativeWeightCalculator


@@ -40,9 +43,9 @@ public RecursiveWeightCalculator(Tangle tangle, SnapshotProvider snapshotProvide

@Override
public UnIterableMap<HashId, Integer> calculate(Hash entryPoint) throws Exception {
UnIterableMap<HashId, Integer> hashWeight = calculateRatingDfs(entryPoint);
UnIterableMap<HashId, Integer> hashWeightMap = calculateRatingDfs(entryPoint);

return hashWeight;
return hashWeightMap;
}

private UnIterableMap<HashId, Integer> calculateRatingDfs(Hash entryPoint) throws Exception {
@@ -54,15 +57,15 @@ private UnIterableMap<HashId, Integer> calculateRatingDfs(Hash entryPoint) throw
// Estimated capacity per depth, assumes 5 minute gap in between milestones, at 3tps
UnIterableMap<HashId, Integer> hashWeight = createTxHashToCumulativeWeightMap( 5 * 60 * 3 * depth);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call it hashToWeight or hashWeightMap.
This variable has an integer name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you changed it in one place, but you didn't change it in the others


Map<Hash, HashSet<Hash>> txToDirectApprovers = new HashMap<>();
Map<Hash, Set<Hash>> txToDirectApprovers = new HashMap<>();

Deque<Hash> stack = new ArrayDeque<>();
stack.push(entryPoint);

while (CollectionUtils.isNotEmpty(stack)) {
while (!stack.isEmpty()) {
Hash txHash = stack.peekLast();

HashSet<Hash> approvers = getTxDirectApproversHashes(txHash, txToDirectApprovers);
Set<Hash> approvers = getTxDirectApproversHashes(txHash, txToDirectApprovers);
if (null != approvers && (approvers.size() == 0 || hasAll(hashWeight, approvers, stack))) {
approvers.add(txHash);
hashWeight.put(txHash, getRating(approvers, txToDirectApprovers));
@@ -75,21 +78,36 @@ private UnIterableMap<HashId, Integer> calculateRatingDfs(Hash entryPoint) throw
return hashWeight;
}

private int getRating(HashSet<Hash> nonDupes, Map<Hash, HashSet<Hash>> txToDirectApprovers) throws Exception {
Deque<Hash> stack = new ArrayDeque<>(nonDupes);
while (CollectionUtils.isNotEmpty(stack)) {
HashSet<Hash> approvers = getTxDirectApproversHashes(stack.pollLast(), txToDirectApprovers);
/**
* Gets the rating of a set, calculated by checking its approvers
*
* @param startingSet
* @param txToDirectApproversCache
* @return
* @throws Exception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fill in the @param and @return
If you decided to write a javadoc, do it correctly

*/
private int getRating(Set<Hash> startingSet, Map<Hash, Set<Hash>> txToDirectApproversCache) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the name to getWeight.

Optional:
I think it will be cleaner to add + 1 when you return at the end instead. Or conversely instead of startingSet you can have startingHash or entryHash. But then you will have that getApprovers call that you did before in calculateRatingDfs. It is presumably not so bad because you have the cache and the code will become more readable (you get the weight of the tx in the param).

Deque<Hash> stack = new ArrayDeque<>(startingSet);
while (stack.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After seeing this test I ran the unit tests locally

They failed...

Set<Hash> approvers = getTxDirectApproversHashes(stack.pollLast(), txToDirectApproversCache);
for (Hash hash : approvers) {
if (nonDupes.add(hash)) {
if (startingSet.add(hash)) {
stack.add(hash);
}
}
}

return nonDupes.size();
return startingSet.size();
}

private boolean hasAll(UnIterableMap<HashId, Integer> source, HashSet<Hash> requester, Deque<Hash> stack) {
/**
*
* @param source
* @param requester
* @param stack
* @return
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty javadoc?

private boolean hasAll(UnIterableMap<HashId, Integer> source, Set<Hash> requester, Deque<Hash> stack) {
for (Hash h : requester) {
if (!source.containsKey(h) && !stack.contains(h)) {
return false;
@@ -99,21 +117,27 @@ private boolean hasAll(UnIterableMap<HashId, Integer> source, HashSet<Hash> requ
}

/**
* Finds the approvers of a transaction, and adds it to the txToDirectApprovers map if they werent there yet.
* Finds the approvers of a transaction, and adds it to the txToDirectApprovers map if they weren't there yet.
*
* @param txHash The tx we find the approvers of
* @param txToDirectApprovers The map we look in, and add to
* @param fallback The map we check in before going in the database, can be <code>null</code>
* @return
* @return A set with the direct approvers of the given hash
* @throws Exception
*/
private HashSet<Hash> getTxDirectApproversHashes(Hash txHash, Map<Hash, HashSet<Hash>> txToDirectApprovers)
private Set<Hash> getTxDirectApproversHashes(Hash txHash, Map<Hash, Set<Hash>> txToDirectApprovers)
throws Exception {

HashSet<Hash> txApprovers = txToDirectApprovers.get(txHash);
Set<Hash> txApprovers = txToDirectApprovers.get(txHash);
if (txApprovers == null) {
ApproveeViewModel approvers = ApproveeViewModel.load(tangle, txHash);
Collection<Hash> appHashes = CollectionUtils.emptyIfNull(approvers.getHashes());
Collection<Hash> appHashes;
if (approvers == null || approvers.getHashes() == null) {
appHashes = Collections.emptySet();
} else {
appHashes = approvers.getHashes();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is cleaner to write it this way:

 Collection<Hash> appHashes = (approvers == null || approvers.getHashes() == null) 
                                                     ? Collections.emptySet()
                                                     : approvers.getHashes();

Only change if you agree, you don't have to. I care less about that. It is just less lines.


txApprovers = new HashSet<>(appHashes.size());
for (Hash appHash : appHashes) {
// if not genesis (the tx that confirms itself)
Original file line number Diff line number Diff line change
@@ -283,7 +283,6 @@ public void testTangleWithCircle2() throws Exception {
}

@Test
@Ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using the prefix again?
How come this is passing?

When I asked before I just didn't check what this unit test does.
Then you reminded and convinced me that this should be ignored and probably deleted later once we finalize tipsel.

public void testCollsionsInDiamondTangle() throws Exception {
TransactionViewModel transaction, transaction1, transaction2, transaction3;
transaction = new TransactionViewModel(getTransactionTrits(), getTransactionHash());