gramoloss
Version:
Graph theory package for edition and computation
577 lines (473 loc) • 17.7 kB
text/typescript
import { Graph } from "../graph";
import { Option } from "../option";
import { Vertex } from "../vertex";
/**
* @param m is the TRANSPOSED adjacency matrix of a directed graph
* @todo CHECK for loops
*
* An arc u->v is said to be heavy if there exists vertices a, b, c such that
* - a -> b -> c -> a is a cycle
* - a,b,c -> u
* - v -> a,b,c
*
* @returns undefined if there is no heavy arc (in that case the matrix is light)
* @returns [u,v,a,b,c] where u->v is an heavy arc
*/
export function searchHeavyArc(m: Array<Array<boolean>>): Option<Array<number>> {
let n = m.length;
const order = new Array<number>();
for (let u = 0; u < n; u ++){
for (let v = 0; v < n; v ++){
if ( m[v][u] == false) { continue }; // Suppose m[v][u] = true so v <- u
// If u->v is not heavy then the vertices x such that v -> x -> u should be acyclic
// So there should be a partial order for them
// So we reconstruct the partial order incrementally
// If there is a vertex that cannot be inserted without conflicting with the current partial order then there is a triangle
order.splice(0, order.length);
for (let b = 0; b < n ; b ++){
if ( m[b][v] ){ // b <- v
if (m[u][b] == false){ // u <- b
continue;
}
if( u== 4 && v == 1){
console.log("---", b)
}
let i = 0;
while (i < order.length){
const a = order[i];
if ( m[a][b] ){ // if a <- b break, it is the first in order, so order[k] -> b for every k<i
break;
}
i ++;
}
let isCycle = false;
let j = i+1;
while (j < order.length){
const c = order[j];
if ( m[b][c] ){ // b <- order[j]
isCycle = true;
break;
}
j ++;
}
if( u== 4 && v == 1){
console.log(i, j, order, isCycle)
}
if (isCycle){
return [u,v,order[j],b,order[i]];
} else {
order.splice( i, 0, b);
}
}
}
}
}
return undefined;
}
/**
* We suppose u->v
* @param outNeighbors
* @param inNeighbors
* @param u
* @param v
* @returns
*/
export function searchHeavyArcDigraphAUXlocal(outNeighbors: Array<Array<number>>, inNeighbors: Array<Array<number>>, u: number, v: number ): Array<number>{
const n = outNeighbors.length;
// Type 1 conflict with uv (u->v is an heavy arc)
const vertices = new Array<number>();
for (const x of inNeighbors[u]){
if (outNeighbors[v].includes(x)){
vertices.push(x);
}
}
for (const a of vertices){
for (const b of vertices){
if (outNeighbors[a].includes(b)){
for (const c of vertices){
if (outNeighbors[b].includes(c) && inNeighbors[a].includes(c)){
// console.log("type 1")
return [u,v,a,b,c]
}
}
}
}
}
// Type 2: uv is in the triangle (search for w the third vertex of the triangle)
for (const w of outNeighbors[v]){
if (outNeighbors[w].includes(u)){
const dominated = new Array();
for (const a of outNeighbors[v]){
if (outNeighbors[u].includes(a) && outNeighbors[w].includes(a)){
dominated.push(a);
}
}
for (const b of inNeighbors[v]){
if (inNeighbors[u].includes(b) && inNeighbors[w].includes(b)){
for (const a of dominated){
if (outNeighbors[a].includes(b)){
// console.log("type 2")
return [a,b,u,v,w]
}
}
}
}
}
}
// Type 3: v is in the triangle and u is endvertex of the searched heavy arc
for (const w of outNeighbors[v]){
if (outNeighbors[u].includes(w)){
for (const x of outNeighbors[w]){
if (outNeighbors[u].includes(x) && outNeighbors[x].includes(v)){
for (const y of outNeighbors[v]){
if (outNeighbors[x].includes(y) && outNeighbors[w].includes(y) && outNeighbors[y].includes(u)){
// console.log("type 3")
return [y,u,v,w,x]
}
}
}
}
}
}
// Type 4: u is in the triangle and v is the start vertex of the searched heavy arc
for (const w of outNeighbors[u]){
if (outNeighbors[w].includes(v)){
for (const x of outNeighbors[w]){
if (outNeighbors[x].includes(v) && outNeighbors[x].includes(u)){
for (const y of outNeighbors[v]){
if (outNeighbors[y].includes(u) && outNeighbors[y].includes(w) && outNeighbors[y].includes(x)){
// console.log("type 4")
return [v,y,u,w,x]
}
}
}
}
}
}
return []
}
export function searchLocalHeavyArcMatrix(matrix: Array<Array<boolean>>, u: number, v: number ): Array<number>{
const n = matrix.length;
// Type 1 conflict with uv (u->v is an heavy arc)
const vertices = new Array<number>();
for (let x = 0; x < n ; x ++){
if (matrix[x][u] && matrix[v][x]){
vertices.push(x);
}
}
for (const a of vertices){
for (const b of vertices){
if (matrix[a][b]){
for (const c of vertices){
if (matrix[b][c] && matrix[c][a]){
// console.log("type 1")
return [u,v,a,b,c]
}
}
}
}
}
// Type 2: uv is in the triangle (search for w the third vertex of the triangle)
for (let w = 0; w < n ; w ++){
if (matrix[v][w] && matrix[w][u]){
const dominated = new Array();
for (let a = 0; a < n ; a ++){
if (matrix[v][a] && matrix[u][a] && matrix[w][a]){
dominated.push(a);
}
}
for (let b = 0 ; b < n ; b ++){
if (matrix[b][v] && matrix[b][u] && matrix[b][w]){
for (const a of dominated){
if (matrix[a][b]){
// console.log("type 2")
return [a,b,u,v,w]
}
}
}
}
}
}
// Type 3: v is in the triangle and u is endvertex of the searched heavy arc
for (let w = 0; w < n; w ++){
if (matrix[v][w] && matrix[u][w]){
for (let x = 0; x < n ; x ++){
if (matrix[w][x] && matrix[u][x] && matrix[x][v]){
for (let y = 0; y < n ; y ++){
if (matrix[v][y] && matrix[x][y] && matrix[w][y] && matrix[y][u]){
// console.log("type 3")
return [y,u,v,w,x]
}
}
}
}
}
}
// Type 4: u is in the triangle and v is the start vertex of the searched heavy arc
for (let w = 0; w < n ; w ++){
if (matrix[u][w] && matrix[w][v]){
for (let x = 0; x < n ; x ++){
if (matrix[w][x] && matrix[x][v] && matrix[x][u]){
for (let y = 0; y < n ; y ++){
if (matrix[v][y] && matrix[y][u] && matrix[y][w] && matrix[y][x]){
// console.log("type 4")
return [v,y,u,w,x]
}
}
}
}
}
}
return []
}
export function searchHeavyArcDigraphAUX(g: Graph): Array<Vertex>{
for (const u of g.vertices.values()){
for (const v of u.outNeighbors.values()){
const vertices = new Array<Vertex>();
for (const x of u.inNeighbors.values()){
if (v.outNeighbors.has(x.index)){
vertices.push(x);
}
}
for (const a of vertices){
for (const b of vertices){
if (a.outNeighbors.has(b.index)){
for (const c of vertices){
if (b.outNeighbors.has(c.index) && a.inNeighbors.has(c.index)){
return [u,v,a,b,c]
}
}
}
}
}
}
}
return []
}
export function searchHeavyArcDigraph(g: Graph): Array<Vertex>{
return searchHeavyArcDigraphAUX(g);
}
/**
* A tournament is light if there is no arc uv such that there exists a cycle abc such that abc dominates u and v dominates abcs.
* If you want to get a conflict, if there is some, use the function tournamentLightConflict
* @param g
* @returns
*/
export function isTournamentLight(g: Graph): boolean {
// const m = g.getDirectedMatrix();
return searchHeavyArcDigraph(g).length == 0;
}
function findDeductions(outNeighbors: Array<Array<number>>, inNeighbors: Array<Array<number>>, u: number, v: number): Array<[number,number]>{
const n = outNeighbors.length;
const pairs = new Array();
const inDominators = new Array();
const outDominators = new Array();
for (let w = 0; w < n; w ++){
if( outNeighbors[v].includes(w) && inNeighbors[u].includes(w)){
inDominators.splice(0, inDominators.length)
outDominators.splice(0, outDominators.length);
for (let a = 0; a < n ; a ++){
let c = 0;
for (const x of outNeighbors[a]){
if (x == u || x == v || x == w){
c ++;
}
}
if (c == 3){
inDominators.push(a);
for (const b of outDominators){
if ( outNeighbors[a].includes(b) == false){
pairs.push([a,b]);
}
}
continue;
}
c = 0;
for (const x of inNeighbors[a]){
if (x == u || x == v || x == w){
c ++;
}
}
if (c == 3){
outDominators.push(a);
for (const b of inDominators){
if ( inNeighbors[a].includes(b) == false){
pairs.push([b,a]);
}
}
}
}
}
}
return pairs;
}
// -------------------
// V 2 with matrix
function findDeductionsMatrix(matrix: Array<Array<boolean>>, u: number, v: number): Array<[number,number]>{
const n = matrix.length;
const pairs = new Array();
const inDominators = new Array();
const outDominators = new Array();
for (let w = 0; w < n; w ++){
if( matrix[v][w] && matrix[w][u] ){
inDominators.splice(0, inDominators.length)
outDominators.splice(0, outDominators.length);
for (let a = 0; a < n ; a ++){
let c = 0;
for (let x = 0; x < n ; x ++){
if (matrix[a][x] && (x == u || x == v || x == w)){
c ++;
}
}
if (c == 3){
inDominators.push(a);
for (const b of outDominators){
if (matrix[a][b] == false){
pairs.push([a,b]);
}
}
continue;
}
c = 0;
for (let x = 0; x < n ; x ++){
if (matrix[x][a] && (x == u || x == v || x == w)){
c ++;
}
}
if (c == 3){
outDominators.push(a);
for (const b of inDominators){
if ( matrix[b][a] == false){
pairs.push([b,a]);
}
}
}
}
}
}
return pairs;
}
export function hasLightTournamentExtension2(g: Graph): [boolean, Array<[number, number, boolean, Array<[number, number]>, boolean]>] {
const n = g.vertices.size;
const m = new Array<Array<boolean>>(n);
for (let i = 0; i < n ; i ++){
m[i] = new Array<boolean>(n)
}
const todo = new Array<[number, number, boolean]>();
for (let i = 0; i < n ; i ++){
for (let j = i+1; j < n ; j ++){
if (m[i][j] == false && m[j][i] == false){
todo.push([i,j,false]);
}
}
}
// console.log(todo);
const done = new Array<[number, number, boolean, Array<[number, number]>, boolean]>();
while (true){
// console.log("-------")
// console.log("todo", todo)
// console.log("done", done);
let edge = todo.pop();
if (typeof edge == "undefined"){
// console.log(done)
return [true, done];
}
let [u,v, b] = edge;
if (b == false){
if (m[u][v]){
done.push([u,v,false, [], true])
// console.log("yo")
continue;
}
} else {
if (m[v][u]){
done.push([u,v,true, [], true])
// console.log("ya")
continue;
}
}
if (b == false){
if (g.matrix[u][v] > 0){
console.log("BUG",u, v)
}
m[u][v] = true;
if (searchLocalHeavyArcMatrix(m, u, v).length > 0){
m[u][v] = false;
todo.push([u,v,true])
} else {
const pairs = findDeductionsMatrix(m, u, v);
for (const [a,b] of pairs){
m[a][b] = true;
}
let isDeductionOK = true;
for (const [a,b] of pairs){
if (searchLocalHeavyArcMatrix(m,u,v).length > 0){
isDeductionOK = false;
}
}
if (isDeductionOK == false){
m[u][v] = false;
for (const [a,b] of pairs){
m[a][b] = false;
}
todo.push([u,v,true])
} else {
done.push([u,v,false, pairs, false])
}
}
} else {
m[v][u] = true;
let isBad = searchLocalHeavyArcMatrix(m, v, u).length > 0;
let deductions = new Array<[number,number]>;
let isDeductionOK = true;
if (isBad == false){
deductions = findDeductionsMatrix(m, u, v);
for (const [a,b] of deductions){
m[a][b] = true;
}
for (const [a,b] of deductions){
if (searchLocalHeavyArcMatrix(m, a, b).length > 0){
isDeductionOK = false;
}
}
}
if (isBad || isDeductionOK == false ){
m[v][u] = false
for (const [a,b] of deductions){
m[a][b] = false;
}
todo.push([u,v,false])
let i = 0;
// backtrack
while(true){
i ++;
const pair = done.pop()
if (typeof pair == "undefined"){
return [false, []];
} else {
const [x,y,t, pairs, isDeduced] = pair;
if (isDeduced){
continue;
}
for (const [a,b] of pairs){
m[a][b] = false;
}
if (t == false){
m[x][y] = false;
todo.push([x,y,true])
break
}
else {
m[y][x] = false
todo.push([x,y,false])
}
}
}
} else {
done.push([u,v,true, deductions, false])
}
}
}
console.log("empty graph case")
return [true, []];
}