S2-group / NAPPA

Implementation of a navigation-aware technique for personalized prefetching of network requests of Android apps
MIT License
4 stars 4 forks source link

Fix these items to complete the strategy implement... #78

Closed github-actions[bot] closed 4 years ago

github-actions[bot] commented 4 years ago

Fix these items to complete the strategy implementation:

Use approach A (i.e., last N actual sessions)

Update the visit time to accept empty rows (in the observer?)

Address division by zero in this method

https://github.com/S2-group/NAPPA/blob/64c21d02bd8b5fd5e3f4809312a9d58dfacc8e9c/Prefetching-Library/android_prefetching_lib/src/main/java/nl/vu/cs/s2group/nappa/prefetch/GreedyPrefetchingStrategyOnVisitFrequencyAndTime.java#L82


import android.util.Log;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import nl.vu.cs.s2group.nappa.graph.ActivityNode;
import nl.vu.cs.s2group.nappa.util.NappaUtil;

/**
 * This strategy employs a Greedy approach using the time a user spends in the activities
 * and the how frequent the user access the activities to decide which nodes to select.
 * This strategy runs recursively on the children of the current most probable node.
 * Only a single child is select per recursion.
 * <p>
 * This strategy accepts the following configurations:
 * <ul>
 *     <li>{@link PrefetchingStrategyConfigKeys#WEIGHT_FREQUENCY_SCORE}</li>
 *     <li>{@link PrefetchingStrategyConfigKeys#WEIGHT_TIME_SCORE}</li>
 * </ul>
 */
public class GreedyPrefetchingStrategyOnVisitFrequencyAndTime extends AbstractPrefetchingStrategy implements PrefetchingStrategy {
    private static final String LOG_TAG = GreedyPrefetchingStrategyOnVisitFrequencyAndTime.class.getSimpleName();

    private static final float DEFAULT_WEIGHT_FREQUENCY_SCORE = 0.5f;
    private static final float DEFAULT_WEIGHT_TIME_SCORE = 0.5f;

    protected final float weightFrequencyScore;
    protected final float weightTimeScore;

    public GreedyPrefetchingStrategyOnVisitFrequencyAndTime(Map<PrefetchingStrategyConfigKeys, Object> config) {
        super(config);
        Object data;

        data = config.get(PrefetchingStrategyConfigKeys.WEIGHT_FREQUENCY_SCORE);
        weightFrequencyScore = data != null ? Float.parseFloat(data.toString()) : DEFAULT_WEIGHT_FREQUENCY_SCORE;

        data = config.get(PrefetchingStrategyConfigKeys.WEIGHT_TIME_SCORE);
        weightTimeScore = data != null ? Float.parseFloat(data.toString()) : DEFAULT_WEIGHT_TIME_SCORE;

        if ((weightFrequencyScore + weightTimeScore) != 1.0)
            throw new IllegalArgumentException("The sum of the time and frequency weight must be 1!");
    }

    @NonNull
    @Override
    public List<String> getTopNUrlToPrefetchForNode(@NonNull ActivityNode node, Integer maxNumber) {
        Log.d(LOG_TAG, node.activityName + " searching best successors");
        return getTopNUrlToPrefetchForNode(node, 1, new ArrayList<>());
    }

    /**
     * Auxiliary method to recursively find the best successor. This method works in three stages.
     * <p>
     * The first stage is to find the successor of the current node with the best score.
     * If no successor is found or if the score of the best successor is insufficient
     * (i.e., the calculated score is lower than the lower bound threshold), then the recursion
     * stops and the current list of URLs is returned.
     * <p>
     * If the score is higher, then the second stage starts. In this stage the method verifies
     * if there is sufficient budget (i.e., number of URLs) to add all URLs of the best successor.
     * In the case there isn't, only a subset of the URLs of the best successor is added to the URL
     * list.
     * <p>
     * In the third stage the method verifies whether to continue the recursion or return the
     * current URL list. If the URL budget is completely filled, then the URL list is returned,
     * otherwise, the recursion continues by invoking this method with the best successor object,
     * score and current URL list
     *
     * @param node        The current node in the recursion. Either the node that started the
     *                    recursion or of of its descendant
     * @param parentScore The score of the parent node.
     * @param urlList     The list of URLs to prefetch
     * @return The {@code urlList} after completing all recursions
     */
    private List<String> getTopNUrlToPrefetchForNode(@NonNull ActivityNode node, float parentScore, List<String> urlList) {
        // TODO Fix these items to complete the strategy implementation:
        //  Use approach A (i.e., last N actual sessions)
        //  Update the visit time to accept empty rows (in the observer?)
        //  Address division by zero in this method

        // Fetches the data to start the calculations - Aggregate visit time and frequency
        float totalAggregateTime = NappaUtil.getSuccessorsAggregateVisitTime(node);
        int totalAggregateFrequency = NappaUtil.getSuccessorsTotalAggregateVisitFrequency(node, lastNSessions);
        Map<String, Integer> successorsAggregateFrequencyMap = NappaUtil.mapSuccessorsAggregateVisitFrequency(node, lastNSessions);
        // Temporary variables to find the best successor
        ActivityNode bestSuccessor = null;
        float bestSuccessorScore = 0;

        // Loop all successors and saves the successor with the best score. In case of drawn, the
        //  first current best successor is picked
        for (ActivityNode successor : node.successors.keySet()) {
            Integer successorFrequency = successorsAggregateFrequencyMap.get(successor.activityName);
            if (successorFrequency == null)
                throw new NoSuchElementException("Unable to obtain the successor frequency count!");
            float successorTime = successor.getAggregateVisitTime().totalDuration;

            float successorTimeScore = totalAggregateTime == 0 ? 0 : (successorTime / totalAggregateTime * weightTimeScore);
            float successorFrequencyScore = totalAggregateFrequency == 0 ? 0 : ((float) successorFrequency / totalAggregateFrequency * weightFrequencyScore);

            float successorScore = parentScore * (successorTimeScore + successorFrequencyScore);

            if (bestSuccessor == null || successorScore > bestSuccessorScore) {
                bestSuccessor = successor;
                bestSuccessorScore = successorScore;
            }
        }

        // Verifies if this node has any successor. If it has, verifies if the successor with the best score has a score high enough
        if (bestSuccessor == null || bestSuccessorScore < scoreLowerThreshold) return urlList;

        // Fetches the URLs from the bestSuccessor and the remaining URL budget
        List<String> bestSuccessorUrls = NappaUtil.getUrlsFromCandidateNode(node, bestSuccessor);
        int remainingUrlBudget = maxNumberOfUrlToPrefetch - urlList.size();

        Log.d(LOG_TAG, node.activityName +
                " best successor is " + bestSuccessor.activityName +
                " with score " + bestSuccessorScore +
                " containing the URLS " + bestSuccessorUrls);

        // Verifies if the URL list of the best successor fits the budget.
        // If not, take only the first N URLs until using all budget
        if (bestSuccessorUrls.size() > remainingUrlBudget) {
            bestSuccessorUrls = bestSuccessorUrls.subList(0, remainingUrlBudget);
        }

        // Add the remaining URLs to the list of URLs to prefetch
        urlList.addAll(bestSuccessorUrls);

        // Verifies if there is any URL budget left
        if (urlList.size() >= maxNumberOfUrlToPrefetch) return urlList;
        return getTopNUrlToPrefetchForNode(bestSuccessor, bestSuccessorScore, bestSuccessorUrls);
    }
}
ndex 788352d..5926e01 100644
++ b/Prefetching-Library/android_prefetching_lib/src/main/java/nl/vu/cs/s2group/nappa/prefetch/GreedyWithPageRankScoresPrefetchingStrategy.java

a25312bfc53161425e240209175ed6c9438e520f

sshann commented 4 years ago

This TODO was completed at #67 Remove comments and close this issue