Fast way to get edge-list of graph in terms of vertex indices (not vertex names)

Using IndexGraph:

 g = GridGraph[{250, 250}];

 a = Developer`ToPackedArray[
    List @@@ EdgeList[IndexGraph[g]]]; // AbsoluteTiming

{0.063857, Null}

Using AdjacencyMatrix:

b = UpperTriangularize[AdjacencyMatrix[g]][
    "NonzeroPositions"]; // AbsoluteTiming

{0.002584, Null}

c = idxEdgeList[g]; // AbsoluteTiming

{0.276563, Null}

Test results:

Developer`PackedArrayQ /@ {a, b, c}

{True, True, True}

a == b == c

True


Or possibly:

idxEdgeList4[graph_] := 
 Module[{edge = IncidenceMatrix[graph]}, 
  Flatten[Transpose[edge]["ColumnIndices"]]] 

When comparing solution answer with your test sequence and the suggestion from Wolfram Community (Carl Woll)

g1 = GridGraph[{250, 250, 2}];
g2 = Graph@EdgeList[g1];
g3 = GraphComputation`ToGraphRepresentation[g2, "Simple"];
g4 = GraphComputation`ToGraphRepresentation[g2, "Sparse"];
wg1 = Graph[g1, EdgeWeight -> RandomReal[1, EdgeCount[g1]]];
wg2 = Graph[g2, EdgeWeight -> RandomReal[1, EdgeCount[g2]]];

h1 = RandomGraph[{10000, 400000}];
h2 = Graph@EdgeList[h1];
h3 = GraphComputation`ToGraphRepresentation[h2, "Simple"];
h4 = GraphComputation`ToGraphRepresentation[h2, "Sparse"];
wh1 = Graph[h1, EdgeWeight -> RandomReal[1, EdgeCount[h1]]];
wh2 = Graph[h2, EdgeWeight -> RandomReal[1, EdgeCount[h2]]];

e1 = ExampleData[{"NetworkGraph", "CondensedMatterCollaborations"}];
e2 = ExampleData[{"NetworkGraph", "HighEnergyTheoryCollaborations"}];

m1 = Graph[UndirectedEdge @@@ RandomInteger[{1, 2000}, {80000, 2}]];
m2 = GraphComputation`ToGraphRepresentation[m1, "Sparse"];

graphs = AssociationThread[{"g1", "g2", "g3", "g4", "wg1", "wg2", 
    "h1", "h2", "h3", "h4", "wh1", "wh2", "e1", "e2", "m1", 
    "m2"}, {g1, g2, g3, g4, wg1, wg2, h1, h2, h3, h4, wh1, wh2, e1, 
    e2, m1, m2}];  



timings = 
       Table[First@AbsoluteTiming@idxEdgeList4[#], {5}] & /@ 
        graphs; // AbsoluteTiming
    timings // Map[Min] // Normal // Map@Apply[List] // TableForm

    Table[Partition[idxEdgeList4[graphs[[idx]]], 2] == 
      elist[graphs[[idx]]], {idx, 1, Length[graphs] - 2}]

{1.65259, Null}

{
 {"g1", 0.009295},
 {"g2", 0.012675},
 {"g3", 0.008175},
 {"g4", 0.008289},
 {"wg1", 0.009168},
 {"wg2", 0.011549},
 {"h1", 0.033966},
 {"h2", 0.047896},
 {"h3", 0.033908},
 {"h4", 0.033706},
 {"wh1", 0.033781},
 {"wh2", 0.046331},
 {"e1", 0.001917},
 {"e2", 0.000581},
 {"m1", 0.00986},
 {"m2", 0.005835}
}

{True, True, True, True, True, True, True, True, True, True, True, \ True, True, True}

Comparing solutions with:

igEdgeList[graph_] :=
Developer`ToPackedArray@If[GraphComputation`GraphRepresentation[graph] === "Simple",
  Flatten[EdgeList@IndexGraph[graph, 0], 1, If[DirectedGraphQ[graph], DirectedEdge, UndirectedEdge]]
  ,
  Lookup[
    AssociationThread[VertexList[graph], Range@VertexCount[graph] - 1],
    Flatten[EdgeList[graph], 1, If[DirectedGraphQ[graph], DirectedEdge, UndirectedEdge]]
  ]
]

resulted in:

{True, False, True, False, True, False, True, False, True, False, \ True, False, False, False}

Where the differences were mainly to do with numbering (noting that my solution didn't account for Direction) and could be fixed through reversal of solutions or adding +1 to the output of igEdgeList. I haven't checked the FullForm output for these graphs but from past experience the output algorithm orders sometimes differ from those provided by functions like "EdgeList" and further vary between others. In other words unless you check there are often hidden edge cases especially with huge graphs. I have a large file of comparisons between the output of EdgeList and those generated by Matrix Functions and indexes don't always match. e.g. Graph[h2] functions index from 1-131 and skip 1-2, 1-3, etc. I wouldn't be surprised if on some cases what is being generated and shown in Mathematica is different from that generated by the functions we are creating from first principles but who has the time to actually prove that for every case?

Elaboration: In response to Szabolics "... but from past experience the output algorithm orders sometimes differ from those provided by functions like "EdgeList" ... "

Yes, my comment was in relation to edge ordering and the general problem of validating output in Mathematica. Or rather accepting at face value that the functions work as advertised. From simple inspection I can see that the ordering may be different (why does numbering differ?). Given there is this question I am not sure as to what the correct output may be and from what function it may be derived and whether \[Equal] breaks down for certain cases or large solutions. I just wanted to be upfront about the fact I wasn't sure whether things were breaking down for your test cases as I wasn't sure which output to validate against.

In the past I had huge problems when I was trying to speed up Voronoi solutions and Meshfunctions. At the time the fastest implementation was parsing the Fullform version of the graphical output, rather than the list version... It turned out I needed to validate every analysis output, as for certain large solutions the were problems with vertice order (and both outputs differed)... More recently I submitted a bug concerning clustering on datasets and the answer came back that the issue was a known bug and the function would simply work better with large datasets and I should just trust the output.

Aside from the edge ordering there appeared to be "issues" with how functions operated across datasets that contain multiple graphs or disjointedgraphs. To check the correct output, one may need to break down the set into its disparate graphs and validate against each.

But it may all be fine. For one the complexity of some graphs is so off the charts equivalency may just be near enough is good enough. Also if your just trying to export maybe the representation does't really matter so long as you can prove that reversing it results in the original graph:)