/
githubmirror
/
oppia
Обзор
Документация
Войти
/
githubmirror
/
oppia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
extensions/interactions/GraphInput/directives/graph-input-rules.service.ts
259 строк
8 KB
Hardik Goyal
Fix #7092: Remove downgradeComponent (#21903)
18 фев 2025, 21:22
Не верифицирован
18 фев 2025, 21:22
4dbd84c
Код
Авторство
О чём код?
// Copyright 2019 The Oppia Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Rules service for the interaction. */ import {Injectable} from '@angular/core'; import { AdjacencyMatrix, GraphUtilsService, } from 'interactions/GraphInput/directives/graph-utils.service'; import {UtilsService} from 'services/utils.service'; import {GraphAnswer} from 'interactions/answer-defs'; import { GraphIsomorphicRuleInputs, GraphPropertyRuleInputs, } from 'interactions/rule-input-defs'; @Injectable({ providedIn: 'root', }) export class GraphInputRulesService { constructor( private gus: GraphUtilsService, private utilsService: UtilsService ) {} /** * @param {object} graph - A graph object. * @return {boolean} Whether the graph is strongly connected. */ private isStronglyConnected(graph: GraphAnswer): boolean { // Uses depth first search on each vertex to try and visit every other // vertex in both the normal and inverted adjacency lists. if (graph.vertices.length === 0) { return true; } var adjacencyLists = this.gus.constructAdjacencyLists( graph, this.gus.GRAPH_ADJACENCY_MODE.DIRECTED ); var invertedAdjacencyLists = this.gus.constructAdjacencyLists( graph, this.gus.GRAPH_ADJACENCY_MODE.INVERTED ); var isVisited = graph.vertices.map(function () { return false; }); this.gus.markAccessible(0, adjacencyLists, isVisited); var isAnyVertexUnreachable = isVisited.some(function (visited) { return visited === false; }); var isVisitedInReverse = graph.vertices.map(function () { return false; }); this.gus.markAccessible(0, invertedAdjacencyLists, isVisitedInReverse); var isAnyVertexUnreachableInReverse = isVisitedInReverse.some( function (visited) { return visited === false; } ); return !isAnyVertexUnreachable && !isAnyVertexUnreachableInReverse; } /** * @param {object} graph - A graph object. * @return {boolean} Whether the graph is weakly connected. */ private isWeaklyConnected(graph: GraphAnswer): boolean { // Generates adjacency lists assuming graph is undirected, then uses depth // first search on node 0 to try to reach every other vertex. if (graph.vertices.length === 0) { return true; } var adjacencyLists = this.gus.constructAdjacencyLists( graph, this.gus.GRAPH_ADJACENCY_MODE.UNDIRECTED ); var isVisited = graph.vertices.map(function () { return false; }); this.gus.markAccessible(0, adjacencyLists, isVisited); return isVisited.every(function (visited) { return visited === true; }); } /** * @param {object} graph - A graph object. * @return {boolean} Whether the graph is acyclic. */ private isAcyclic(graph: GraphAnswer): boolean { // Uses depth first search to ensure that we never have an edge to an // ancestor in the search tree. var nodeStatus = graph.vertices.map(() => { return this.gus.DFS_STATUS.UNVISITED; }); var adjacencyLists = this.gus.constructAdjacencyLists( graph, this.gus.GRAPH_ADJACENCY_MODE.DIRECTED ); for ( var startVertex = 0; startVertex < graph.vertices.length; startVertex++ ) { if (nodeStatus[startVertex] === this.gus.DFS_STATUS.UNVISITED) { if ( this.gus.findCycle( startVertex, -1, adjacencyLists, nodeStatus, graph.isDirected ) ) { return false; } } } return true; } /** * @param {object} graph - A graph object. * @return {boolean} Whether the graph is acyclic. */ private isRegular(graph: GraphAnswer): boolean { // Checks that every vertex has outdegree and indegree equal to the first. if (graph.vertices.length === 0) { return true; } var adjacencyLists = this.gus.constructAdjacencyLists( graph, this.gus.GRAPH_ADJACENCY_MODE.DIRECTED ); var outdegreeCounts = adjacencyLists.map(function (list) { return list.length; }); var indegreeCounts = adjacencyLists.map(function () { return 0; }); adjacencyLists.forEach(function (list) { list.forEach(function (destination) { indegreeCounts[destination]++; }); }); var areIndegreeCountsEqual = indegreeCounts.every(function (indegree) { return indegree === indegreeCounts[0]; }); var areOutdegreeCountsEqual = outdegreeCounts.every(function (outdegree) { return outdegree === outdegreeCounts[0]; }); return areIndegreeCountsEqual && areOutdegreeCountsEqual; } private _getDegreesOfMatrix(adj: AdjacencyMatrix): number[] { return adj .map(value => { return value.reduce((prev, cur) => { prev = prev === null ? 0 : prev; cur = cur === null ? 0 : cur; return prev + cur; }); }) .sort() as number[]; } private isIsomorphic(graph1: GraphAnswer, graph2: GraphAnswer): boolean { if (graph1.vertices.length !== graph2.vertices.length) { return false; } var adj1 = this.gus.constructAdjacencyMatrix(graph1); var adj2 = this.gus.constructAdjacencyMatrix(graph2); // Check that for every vertex from the first graph there is a vertex in // the second graph with the same sum of weights of outgoing edges. var degrees1 = this._getDegreesOfMatrix(adj1); var degrees2 = this._getDegreesOfMatrix(adj2); if (!this.utilsService.isEquivalent(degrees1, degrees2)) { return false; } // Check against every permutation of vectices. var numVertices = graph2.vertices.length; // The permutation will be 'null' when there are no more // lexicographical permutation of vertices. var permutation: number[] | null = []; for (var i = 0; i < numVertices; i++) { permutation.push(i); } while (permutation !== null) { var doLabelsMatch = (!graph1.isLabeled && !graph2.isLabeled) || graph2.vertices.every((vertex, index) => { const _permutation = permutation as number[]; return vertex.label === graph1.vertices[_permutation[index]].label; }); if ( doLabelsMatch && this.gus.areAdjacencyMatricesEqualWithPermutation( adj1, adj2, permutation ) ) { return true; } permutation = this.gus.nextPermutation(permutation); } return false; } HasGraphProperty( answer: GraphAnswer, inputs: GraphPropertyRuleInputs ): boolean { if (inputs.p === 'strongly_connected') { return this.isStronglyConnected(answer); } else if (inputs.p === 'weakly_connected') { return this.isWeaklyConnected(answer); } else if (inputs.p === 'acyclic') { return this.isAcyclic(answer); } else if (inputs.p === 'regular') { return this.isRegular(answer); } else { return false; } } IsIsomorphicTo( answer: GraphAnswer, inputs: GraphIsomorphicRuleInputs ): boolean { return this.isIsomorphic(answer, inputs.g); } }