def test_collapse_degree_2_vtxs():
    g = igraph.Graph(directed=True)
    # a square with a diagonal + tail
    g.add_vertices(5)
    # the square
    g.add_edges([
        (0, 1),
        (1, 2),
        (2, 3),
        (3, 0),
    ])
    # the diagonal
    g.add_edges([(0, 2)])
    # the tail
    g.add_edges([(1, 4)])
    g.es["weight"] = range(6)
    # so now vertex 3 is just a thru-node
    eq_(len(g.vs), 5)
    eq_(len(g.es), 6)

    eq_(len(g.vs.select(_degree_eq=1)), 1)
    eq_(len(g.vs.select(_degree_eq=2)), 1)
    eq_(len(g.vs.select(_degree_eq=3)), 3)

    ret = gt.collapse_degree_2_vtxs(g)
    eq_(ret, 1)
    # Now it is clean
    eq_(len(g.vs), 4)
    eq_(len(g.es), 5)
    eq_(len(g.vs.select(_degree_eq=1)), 1)
    eq_(len(g.vs.select(_degree_eq=2)), 0)
    eq_(len(g.vs.select(_degree_eq=3)), 3)
def test_collapse_degree_2_vtxs_bollard():
    # We should not remove nodes are source/sink only.
    g = igraph.Graph(directed=True)
    # two boxes
    g.add_vertices(6)
    # the outer rectange
    g.add_edges([
        (1, 0),
        (0, 5),

        (2, 1),
        (2, 3),

        (3, 4),
        (4, 5),
    ])
    # the center line
    g.add_edges([(4, 1)])

    eq_(g.vs[0].indegree(), 1)
    eq_(g.vs[0].outdegree(), 1)

    eq_(g.vs[5].indegree(), 2)
    eq_(g.vs[5].outdegree(), 0)

    eq_(g.vs[1].indegree(), 2)
    eq_(g.vs[1].outdegree(), 1)

    eq_(g.vs[2].indegree(), 0)
    eq_(g.vs[2].outdegree(), 2)

    eq_(g.vs[3].indegree(), 1)
    eq_(g.vs[3].outdegree(), 1)

    eq_(g.vs[4].indegree(), 1)
    eq_(g.vs[4].outdegree(), 2)

    # so now vertex 0 and 3 are thru-ways
    # vertex 5 and 2 are bollards
    eq_(len(g.vs), 6)
    eq_(len(g.es), 7)
    g.es["weight"] = range(7)

    assert(gt.is_bollard(g.vs[2]))
    assert(gt.is_bollard(g.vs[5]))
    assert(not gt.is_bollard(g.vs[1]))
    assert(not gt.is_bollard(g.vs[3]))

    ret = gt.collapse_degree_2_vtxs(g)
    # deleted 4
    eq_(ret, 2)

    # Now it is clean
    eq_(len(g.vs), 4)
    print [es.tuple for es in g.es]
    eq_(len(g.es), 5)
    eq_(len(g.vs.select(_degree_eq=2, _indegree_gt=0, _outdegree_gt=0)), 0)
def test_collapse_degree_2_vtxs_bollard():
    # We should not remove nodes are source/sink only.
    g = igraph.Graph(directed=True)
    # two boxes
    g.add_vertices(6)
    # the outer rectange
    g.add_edges([
        (1, 0),
        (0, 5),

        (2, 1),
        (2, 3),

        (3, 4),
        (4, 5),
    ])
    # the center line
    g.add_edges([(4, 1)])

    eq_(g.vs[0].indegree(), 1)
    eq_(g.vs[0].outdegree(), 1)

    eq_(g.vs[5].indegree(), 2)
    eq_(g.vs[5].outdegree(), 0)

    eq_(g.vs[1].indegree(), 2)
    eq_(g.vs[1].outdegree(), 1)

    eq_(g.vs[2].indegree(), 0)
    eq_(g.vs[2].outdegree(), 2)

    eq_(g.vs[3].indegree(), 1)
    eq_(g.vs[3].outdegree(), 1)

    eq_(g.vs[4].indegree(), 1)
    eq_(g.vs[4].outdegree(), 2)

    # so now vertex 0 and 3 are thru-ways
    # vertex 5 and 2 are bollards
    eq_(len(g.vs), 6)
    eq_(len(g.es), 7)
    g.es["weight"] = range(7)

    assert(gt.is_bollard(g.vs[2]))
    assert(gt.is_bollard(g.vs[5]))
    assert(not gt.is_bollard(g.vs[1]))
    assert(not gt.is_bollard(g.vs[3]))

    ret = gt.collapse_degree_2_vtxs(g)
    # deleted 4
    eq_(ret, 2)

    # Now it is clean
    eq_(len(g.vs), 4)
    print [es.tuple for es in g.es]
def main(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('output',
                        metavar='graph.pickle',
                        help='Ouput graph file')
    parser.add_argument(
        '--connection',
        default='postgresql://*****:*****@localhost/osrm',
        help='Postgres connection string.  Default %(default)s')
    parser.add_argument('--prune',
                        action='store_true',
                        help='Collapse redundant edges, prune tails')

    parser.add_argument('--verbose',
                        action='store_true',
                        help='Increase logging level')

    args = parser.parse_args()

    logging.basicConfig(
        level=logging.INFO if args.verbose else logging.WARNING)

    log.info("Creating DB engine")
    engine = create_engine(args.connection, echo=False)

    log.info("Creating DB session")
    Session = sessionmaker(bind=engine)
    session = Session()

    g = build_graph(session)

    if args.prune:
        log.info("Collapsing unidirectional strings")
        pruned = graphtools.collapse_degree_2_vtxs(g)
        log.info("Removed %i thru-nodes", pruned)
        log.info("Collapsing bidirectional strings")
        pruned = graphtools.collapse_bidirectional_streets(g)
        log.info("Removed %i thru-nodes", pruned)
        log.info("Snipping tails")
        snipped = graphtools.delete_degree_1_vtxs(g)
        log.info("Removed %i tails", snipped)
        loners = graphtools.delete_degree_0_vtxs(g)
        log.info("Removed %i loner-nodes (should be zero)", loners)
        redundancies = graphtools.identify_rendudant_nodes(g)
        log.info("Marked %i nodes as redundant", redundancies)

    log.info("Saving graph to %s", args.output)
    g.save(args.output)
def test_collapse_degree_2_vtxs_complex():
    g = igraph.Graph(directed=True)
    # two boxes
    g.add_vertices(6)
    # the outer rectange
    g.add_edges([
        (0, 1),
        (1, 2),
        (2, 3),
        (3, 4),
        (4, 5),
        (5, 0),
    ])
    # the center line
    g.add_edges([(1, 4)])

    # This makes it so all collapsed edges
    # have conserved flow.
    g.es["weight"] = [
        3,
        4,
        4,
        4,
        3,
        3,
        2
    ]

    eq_(g.es[g.get_eid(1, 4)]["weight"], 2)
    eq_(g.es[g.get_eid(0, 1)]["weight"], 3)
    eq_(g.es[g.get_eid(1, 2)]["weight"], 4)

    # so now vertex 5-0 and 2-3 are thru-ways
    eq_(len(g.vs), 6)
    eq_(len(g.es), 7)

    ret = gt.collapse_degree_2_vtxs(g)
    # deleted 4
    eq_(ret, 4)

    # Now it is clean
    eq_(len(g.vs), 2)
    eq_(len(g.es), 3)
    eq_(len(g.vs.select(_degree_eq=2)), 0)

    # Edge weights should be respected
    eq_(g.es["weight"], [2, 3, 4])
def main(args):
    parser = argparse.ArgumentParser()
    parser.add_argument('output', metavar='graph.pickle',
                        help='Ouput graph file')
    parser.add_argument(
        '--connection',
        default='postgresql://*****:*****@localhost/osrm',
        help='Postgres connection string.  Default %(default)s'
    )
    parser.add_argument('--prune', action='store_true',
                        help='Collapse redundant edges, prune tails')

    parser.add_argument('--verbose', action='store_true',
                        help='Increase logging level')

    args = parser.parse_args()

    logging.basicConfig(
        level=logging.INFO if args.verbose else logging.WARNING)

    log.info("Creating DB engine")
    engine = create_engine(args.connection, echo=False)

    log.info("Creating DB session")
    Session = sessionmaker(bind=engine)
    session = Session()

    g = build_graph(session)

    if args.prune:
        log.info("Collapsing unidirectional strings")
        pruned = graphtools.collapse_degree_2_vtxs(g)
        log.info("Removed %i thru-nodes", pruned)
        log.info("Collapsing bidirectional strings")
        pruned = graphtools.collapse_bidirectional_streets(g)
        log.info("Removed %i thru-nodes", pruned)
        log.info("Snipping tails")
        snipped = graphtools.delete_degree_1_vtxs(g)
        log.info("Removed %i tails", snipped)
        loners = graphtools.delete_degree_0_vtxs(g)
        log.info("Removed %i loner-nodes (should be zero)", loners)
        redundancies = graphtools.identify_rendudant_nodes(g)
        log.info("Marked %i nodes as redundant", redundancies)

    log.info("Saving graph to %s", args.output)
    g.save(args.output)