ditl.sim.pnt.DominatingSetConverter.java Source code

Java tutorial

Introduction

Here is the source code for ditl.sim.pnt.DominatingSetConverter.java

Source

/*******************************************************************************
 * This file is part of DITL.                                                  *
 *                                                                             *
 * Copyright (C) 2011 John Whitbeck <john@whitbeck.fr>                         *
 *                                                                             *
 * DITL is free software: you can redistribute it and/or modify                *
 * it under the terms of the GNU General Public License as published by        *
 * the Free Software Foundation, either version 3 of the License, or           *
 * (at your option) any later version.                                         *
 *                                                                             *
 * DITL is distributed in the hope that it will be useful,                     *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of              *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
 * GNU General Public License for more details.                                *
 *                                                                             *
 * You should have received a copy of the GNU General Public License           *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.       *
 *******************************************************************************/
package ditl.sim.pnt;

import java.io.IOException;
import java.util.*;

import org.apache.commons.cli.*;

import ditl.*;
import ditl.Store.LoadTraceException;
import ditl.Store.NoSuchTraceException;
import ditl.WritableStore.AlreadyExistsException;
import ditl.cli.ConvertApp;
import ditl.graphs.*;
import ditl.graphs.cli.GraphOptions;
import ditl.sim.RNG;

public class DominatingSetConverter extends Bus<Object>
        implements GroupTrace.Handler, PresenceTrace.Handler, Generator, Converter, Listener<Object> {

    private long _delay;
    private long min_time;
    private boolean started = false;
    private static final Integer ds_gid = 0;

    private StatefulWriter<GroupEvent, Group> ds_writer;

    private GroupTrace _ccs;
    private PresenceTrace _presence;
    private GroupTrace _ds;

    private Set<Integer> present = new HashSet<Integer>();
    private Map<Integer, Long> arrivals = new HashMap<Integer, Long>();
    private Map<Integer, Long> departures = new HashMap<Integer, Long>();
    private Map<Integer, Group> groups = new HashMap<Integer, Group>();
    private Map<Integer, Set<Integer>> starting_groups = new HashMap<Integer, Set<Integer>>();
    private Map<Integer, Set<Integer>> node_groups = new HashMap<Integer, Set<Integer>>();
    private Map<Integer, Set<Integer>> current_groups = new HashMap<Integer, Set<Integer>>();

    public DominatingSetConverter(GroupTrace ds, PresenceTrace presence, GroupTrace ccs, long period,
            long minTime) {
        min_time = minTime;
        _ds = ds;
        _presence = presence;
        _ccs = ccs;
        _delay = period;
        addListener(this);
    }

    @Override
    public void incr(long time) throws IOException {
    }

    @Override
    public void seek(long time) throws IOException {
        queue(min_time, Collections.emptySet());
    }

    @Override
    public void convert() throws IOException {
        StatefulReader<GroupEvent, Group> ccs_reader = _ccs.getReader();
        StatefulReader<PresenceEvent, Presence> presence_reader = _presence.getReader();

        ds_writer = _ds.getWriter(_ccs.snapshotInterval());

        ds_writer.setProperty(Trace.ticsPerSecondKey, _ccs.ticsPerSecond());
        ds_writer.setProperty(Trace.minTimeKey, _ccs.minTime());
        ds_writer.setProperty(Trace.maxTimeKey, _ccs.maxTime());

        Bus<Group> groupBus = new Bus<Group>();
        Bus<GroupEvent> groupEventBus = new Bus<GroupEvent>();
        groupBus.addListener(groupListener());
        groupEventBus.addListener(groupEventListener());
        ccs_reader.setBus(groupEventBus);
        ccs_reader.setStateBus(groupBus);

        Bus<Presence> presenceBus = new Bus<Presence>();
        Bus<PresenceEvent> presenceEventBus = new Bus<PresenceEvent>();
        presenceBus.addListener(presenceListener());
        presenceEventBus.addListener(presenceEventListener());
        presence_reader.setBus(presenceEventBus);
        presence_reader.setStateBus(presenceBus);

        Runner runner = new Runner(_ccs.maxUpdateInterval(), _ccs.minTime(), _ccs.maxTime());
        runner.addGenerator(presence_reader);
        runner.addGenerator(ccs_reader);
        runner.addGenerator(this);
        runner.run();

        ds_writer.close();
        ccs_reader.close();
        presence_reader.close();
    }

    @Override
    public Bus<?>[] busses() {
        return new Bus<?>[] { this };
    }

    @Override
    public int priority() {
        return Integer.MAX_VALUE; // this should come after all other events
    }

    private Set<Integer> curDSNodes() {
        for (Group g : ds_writer.states()) {
            return g.members();
        }
        return null;
    }

    @Override
    public void handle(long time, Collection<Object> events) throws IOException {
        if (started) {
            // first handle previous time period
            long t = time - _delay;
            purgeBeforeTime(t, departures); // remove those that departed a time periods ago
            purgeBeforeTime(t, arrivals);
            Set<Integer> ds = new DSCalculator().calculateNewDS();

            Set<Integer> prev_ds_nodes = curDSNodes();
            Set<Integer> joining = new HashSet<Integer>();
            if (prev_ds_nodes != null) {
                for (Integer i : ds)
                    if (!prev_ds_nodes.contains(i))
                        joining.add(i);
                Set<Integer> leaving = new HashSet<Integer>();
                for (Integer i : prev_ds_nodes)
                    if (!ds.contains(i))
                        leaving.add(i);

                if (!leaving.isEmpty())
                    ds_writer.queue(t, new GroupEvent(ds_gid, GroupEvent.LEAVE, leaving));
            } else {
                joining.addAll(ds);
            }

            // handle new ds nodes that arrive or leave during the time period
            for (Iterator<Integer> i = joining.iterator(); i.hasNext();) {
                Integer k = i.next();
                Long arr_time = arrivals.get(k);
                Long dep_time = departures.get(k);
                if (arr_time != null) { // node was present at beginning of time period
                    ds_writer.queue(arr_time, new GroupEvent(ds_gid, GroupEvent.JOIN, new Integer[] { k }));
                    i.remove();
                }
                if (dep_time != null) { // node left during the time period
                    ds_writer.queue(dep_time, new GroupEvent(ds_gid, GroupEvent.LEAVE, new Integer[] { k }));
                }
            }

            if (t == _ccs.minTime()) { // this should be the initial state   
                ds_writer.setInitState(min_time, Collections.singleton(new Group(ds_gid, joining)));
            } else {
                if (ds_writer.states().isEmpty())
                    ds_writer.append(t, new GroupEvent(ds_gid, GroupEvent.NEW));
                if (!joining.isEmpty())
                    ds_writer.queue(t, new GroupEvent(ds_gid, GroupEvent.JOIN, joining));
            }
            ds_writer.flush();

            // then clear state and prepare for new dominating set
            init();

        } else {
            started = true;
            init();
            if (min_time > _ccs.minTime()) // starting after min_time => empty initial state
                ds_writer.setInitState(_ccs.minTime(), Collections.<Group>emptySet());
        }

        queue(time + _delay, Collections.emptySet());
    }

    @Override
    public Listener<Presence> presenceListener() {
        return new Listener<Presence>() {
            @Override
            public void handle(long time, Collection<Presence> events) {
                for (Presence p : events)
                    if (!p.id().equals(MultiplePnT.root_id)) {
                        arrivals.put(p.id(), time);
                        present.add(p.id());
                    }
            }
        };
    }

    @Override
    public Listener<PresenceEvent> presenceEventListener() {
        return new Listener<PresenceEvent>() {
            @Override
            public void handle(long time, Collection<PresenceEvent> events) {
                for (PresenceEvent pev : events) {
                    Integer id = pev.id();
                    if (pev.isIn()) {
                        present.add(id);
                        arrivals.put(id, time);
                    } else {
                        present.remove(id);
                        departures.put(id, time);
                    }
                }
            }
        };
    }

    @Override
    public Listener<GroupEvent> groupEventListener() {
        return new Listener<GroupEvent>() {
            @Override
            public void handle(long time, Collection<GroupEvent> events) {
                Group g;
                Integer gid;
                for (GroupEvent gev : events) {
                    gid = gev.gid();
                    switch (gev.type()) {
                    case GroupEvent.NEW:
                        g = new Group(gid);
                        groups.put(gid, g);
                        break;
                    case GroupEvent.DELETE:
                        groups.remove(gid);
                        break;
                    case GroupEvent.LEAVE:
                        g = groups.get(gid);
                        g.handleEvent(gev);
                        break;
                    case GroupEvent.JOIN:
                        g = groups.get(gid);
                        Set<Integer> gids = getGroupIds(gev.members());
                        if (g.members().isEmpty()) { // we are joining an new group
                            if (gids.isEmpty()) { // several singletons uniting into a new cc
                                Set<Integer> members = new HashSet<Integer>();
                                for (Integer m : gev.members()) {
                                    members.add(m);
                                    node_groups.put(m, new HashSet<Integer>(Collections.singleton(gid)));
                                }
                                starting_groups.put(gid, members);
                                current_groups.put(gid, members);

                            } else { // merge all ids
                                for (Integer k : gev.members())
                                    markNode(k, gids);
                            }
                        } else { // merge all groups
                            Set<Integer> gids2 = getGroupIds(g.members());
                            for (Integer k : g.members()) {
                                markNode(k, gids);
                            }
                            for (Integer k : gev.members()) {
                                markNode(k, gids2);
                            }

                        }
                        g.handleEvent(gev);
                        break;
                    }
                }
            }
        };
    }

    @Override
    public Listener<Group> groupListener() {
        return new Listener<Group>() {
            @Override
            public void handle(long time, Collection<Group> events) {
                for (Group g : events)
                    groups.put(g.gid(), g);
            }
        };
    }

    private Set<Integer> getGroupIds(Iterable<Integer> nids) {
        Set<Integer> union = new HashSet<Integer>();
        for (Integer nid : nids) {
            Set<Integer> gids = node_groups.get(nid);
            if (gids != null)
                union.addAll(gids);
        }
        return union;
    }

    private void markNode(Integer id, Collection<Integer> gids) {
        Set<Integer> cur_gids = node_groups.get(id);
        if (cur_gids == null) {
            node_groups.put(id, new HashSet<Integer>(gids));
        } else {
            cur_gids.addAll(gids);
        }
        for (Integer gid : gids)
            current_groups.get(gid).add(id);
    }

    private void purgeBeforeTime(long time, Map<Integer, Long> map) {
        for (Iterator<Map.Entry<Integer, Long>> i = map.entrySet().iterator(); i.hasNext();) {
            Map.Entry<Integer, Long> e = i.next();
            if (e.getValue() <= time) {
                i.remove();
            }
        }
    }

    private void init() {
        starting_groups.clear();
        current_groups.clear();
        node_groups.clear();
        for (Group g : groups.values()) {
            starting_groups.put(g.gid(), new HashSet<Integer>(g.members()));
            current_groups.put(g.gid(), new HashSet<Integer>(g.members()));
            for (Integer i : g.members())
                node_groups.put(i, new HashSet<Integer>(Collections.singleton(g.gid())));
        }
    }

    class DSCalculator {
        TreeMap<Integer, Set<Integer>> degree_map = new TreeMap<Integer, Set<Integer>>();
        Map<Integer, Set<Integer>> remainders = new HashMap<Integer, Set<Integer>>();
        Set<Integer> covered = new HashSet<Integer>();
        Set<Integer> new_ds = new HashSet<Integer>();

        DSCalculator() {
            // first put all complete singletons
            for (Integer id : present) {
                if (!node_groups.containsKey(id)) { // this node was never marked as being part of a group
                    new_ds.add(id);
                    covered.add(id);
                }
            }
            // then prepare the remainder map
            for (Map.Entry<Integer, Set<Integer>> e : current_groups.entrySet()) {
                Integer gid = e.getKey();
                Set<Integer> r_dest = new HashSet<Integer>(e.getValue());
                remainders.put(gid, r_dest);
                setGroupDegree(gid, r_dest.size());
            }
        }

        Set<Integer> calculateNewDS() {
            while (!allCovered()) {
                pick();
            }
            return new_ds;
        }

        boolean allCovered() {
            return covered.size() >= present.size(); // covered may be greater than the number of present nodes (e.g., edges in reachability traces)
        }

        void pick(Integer gid) {
            Integer id = RNG.randomFromSet(starting_groups.get(gid));
            new_ds.add(id);
            Set<Integer> newly_covered = remainders.remove(gid);
            degree_map.clear();
            for (Map.Entry<Integer, Set<Integer>> e : remainders.entrySet()) {
                Integer grp_id = e.getKey();
                Set<Integer> dests = e.getValue();
                dests.removeAll(newly_covered);
                setGroupDegree(grp_id, dests.size());
            }
            covered.addAll(newly_covered);
        }

        void pick() {
            Integer gid = null;
            Iterator<Integer> i = greedyChoice().iterator();
            while (i.hasNext()) {
                gid = i.next();
                break;
            }
            pick(gid);
        }

        Set<Integer> greedyChoice() {
            return degree_map.lastEntry().getValue();
        }

        void setGroupDegree(Integer node, Integer new_degree) {
            if (!degree_map.containsKey(new_degree))
                degree_map.put(new_degree, new HashSet<Integer>());
            degree_map.get(new_degree).add(node);
        }
    }

    public static void main(String[] args) throws IOException {
        ConvertApp app = new ConvertApp() {

            GraphOptions graph_options = new GraphOptions(GraphOptions.GROUPS, GraphOptions.PRESENCE);
            Long min_time;
            long delay;
            String ds_name;
            final static String dsNameOption = "ds-name";

            @Override
            protected void parseArgs(CommandLine cli, String[] args)
                    throws ParseException, ArrayIndexOutOfBoundsException, HelpException {
                super.parseArgs(cli, args);
                graph_options.parse(cli);
                delay = Long.parseLong(args[1]);
                ds_name = cli.getOptionValue(dsNameOption);
                if (cli.hasOption(minTimeOption))
                    min_time = Long.parseLong(cli.getOptionValue(minTimeOption));
            }

            @Override
            protected void initOptions() {
                super.initOptions();
                graph_options.setOptions(options);
                options.addOption(null, minTimeOption, true, "Start first ds at time <arg>");
                options.addOption(null, dsNameOption, true, "Name of dominating set trace");
            }

            @Override
            protected void run()
                    throws IOException, NoSuchTraceException, AlreadyExistsException, LoadTraceException {
                GroupTrace ccs = (GroupTrace) orig_store.getTrace(graph_options.get(GraphOptions.GROUPS));
                PresenceTrace presence = (PresenceTrace) orig_store
                        .getTrace(graph_options.get(GraphOptions.PRESENCE));
                String name = (ds_name == null) ? GroupTrace.defaultName : ds_name;
                GroupTrace ds = (GroupTrace) dest_store.newTrace(name, GroupTrace.type, force);
                delay *= ccs.ticsPerSecond();
                if (min_time == null)
                    min_time = presence.minTime();
                else
                    min_time *= ccs.ticsPerSecond();
                new DominatingSetConverter(ds, presence, ccs, delay, min_time).convert();
            }

            @Override
            protected String getUsageString() {
                return "[OPTIONS] STORE PERIOD";
            }
        };
        app.ready("", args);
        app.exec();
    }
}