コード例 #1
0
ファイル: GraphForm.cs プロジェクト: unitycoder/GodComplex
        float[][]       ComputeHeatDiffusionEuler(MathSolvers.MatrixF _laplacian, float _diffusionCoefficient, float _timeStep, uint _iterationsCount)
        {
            uint nodesCount = _laplacian.ColumnsCount;

//          // 1] Pre-compute many little laplacian matrices for each node
//          MathSolvers.MatrixF[]	laplacians = new MathSolvers.MatrixF[nodesCount];
//          MathSolvers.MatrixF[]	eigenVectorss = new MathSolvers.MatrixF[nodesCount];
//          MathSolvers.VectorF[]	eigenValuess = new MathSolvers.VectorF[nodesCount];
//          for ( uint nodeIndex=0; nodeIndex < nodesCount; nodeIndex++ ) {
//
//              // 1.1) Count the amount of neighbors
//              uint	j = 0;
//              uint	neighborsCount = 0;
//              for ( ; j < nodeIndex; j++ )
//                  if ( _laplacian[nodeIndex,j] != 0.0f )
//                      neighborsCount++;
//              for ( j++; j < nodesCount; j++ )
//                  if ( _laplacian[nodeIndex,j] != 0.0f )
//                      neighborsCount++;
//
//              // 1.2) Create a tiny matrix for the current node and its neighbors only
//              MathSolvers.MatrixF	laplacian = new MathSolvers.MatrixF( 1+neighborsCount, 1+neighborsCount );
//              laplacians[nodeIndex] = laplacian;
//              laplacian[0,0] = neighborsCount;	// Write degree for central node
//              for ( j=0; j < neighborsCount; j++ ) {
//                  laplacian[j,j] = 1;				// Connected to central node only
//                  laplacian[0,j] = -1;
//                  laplacian[j,0] = -1;
//              }
//
//              // 1.3) Compute eigen vectors for the tiny matrix
//              MathSolvers.SVD	SVD = new MathSolvers.SVD( laplacian );
//              SVD.Decompose();
//              eigenVectorss[nodeIndex] = SVD.U;
//              eigenValuess[nodeIndex] = SVD.w;
//          }

            // 1] Precompute neighbor indices for each node
            List <uint> tempNeighborIndices = new List <uint>((int)nodesCount);

            uint[][] neighborIndicess = new uint[nodesCount][];
            for (uint nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++)
            {
                tempNeighborIndices.Clear();

                // Collect neighbor indices
                uint j = 0;
                for ( ; j < nodeIndex; j++)
                {
                    if (_laplacian[nodeIndex, j] != 0.0f)
                    {
                        tempNeighborIndices.Add(j);
                    }
                }
                for (j++; j < nodesCount; j++)
                {
                    if (_laplacian[nodeIndex, j] != 0.0f)
                    {
                        tempNeighborIndices.Add(j);
                    }
                }

                neighborIndicess[nodeIndex] = tempNeighborIndices.ToArray();
            }

            // 2] Apply diffusion iteratively using Euler method
            float[][] results = new float[_iterationsCount][];

            float[][] heats = new float[2][] {
                new float[nodesCount],
                new float[nodesCount]
            };
            heats[0][0] = 1.0f;

            float diffusionCoefficient = _diffusionCoefficient * _timeStep;

            float[] tempVector = new float[nodesCount];
            for (uint iterationIndex = 0; iterationIndex < _iterationsCount; iterationIndex++)
            {
                float[] sourceHeats = heats[0];
                float[] targetHeats = heats[1];

                for (uint nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++)
                {
                    uint[] neighborNodeIndices = neighborIndicess[nodeIndex];
                    uint   neighborsCount      = (uint)neighborNodeIndices.Length;

                    float sourceHeat = sourceHeats[nodeIndex];

                    // Compute laplacian for this node
                    float laplacian = -neighborsCount * sourceHeat;
                    for (uint neighborIndex = 0; neighborIndex < neighborsCount; neighborIndex++)
                    {
                        uint neighborNodeIndex = neighborNodeIndices[neighborIndex];
                        laplacian += sourceHeats[neighborNodeIndex];
                    }

                    // Apply small heat diffusion
                    targetHeats[nodeIndex] = sourceHeat + diffusionCoefficient * laplacian;
                }

                // Swap heat buffers
                float[] temp = heats[0];
                heats[0] = heats[1];
                heats[1] = temp;

                // Copy each iteration
                float[] result = new float[nodesCount];
                Array.Copy(heats[0], result, nodesCount);
                results[iterationIndex] = result;
            }

            return(results);
        }
コード例 #2
0
ファイル: GraphForm.cs プロジェクト: unitycoder/GodComplex
        //////////////////////////////////////////////////////////////////////////
        // Here we're going to test if the problem of heat diffusion across a graph is separable into multiple small sub-problems
        //
        //	• We first create a small graph of ~100 nodes
        //	• Next we build the full graph Laplacian matrix and compute the eigen vectors
        //	• Then we apply heat diffusion along time
        //
        //	• For the 2nd part, we separate each node into its own little sub-Laplacian matrix with only the connections to its direct neighbors
        //	• We compute the eigen vectors/values for each small matrix
        //	• We apply heat diffusion interatively node by node and we compare the results
        //
        void            GraphSeparabilityTest()
        {
            const int NODES_COUNT         = 100;
            const int MAX_NEIGHBORS_COUNT = 6;

            const float HEAT_DIFFUSION   = 0.8f;
            const float TIME_STEP        = 0.1f;
            const int   ITERATIONS_COUNT = 100;

            //////////////////////////////////////////////////////////////////////////
            // 1] Construct a graph with 100 nodes
            MathSolvers.MatrixF laplacian = new MathSolvers.MatrixF((uint)NODES_COUNT, (uint)NODES_COUNT);

            Random RNG = new Random(1);
            int    tentativeEdgesCount = 0;

            for (int nodeIndex = 0; nodeIndex < NODES_COUNT; nodeIndex++)
            {
                int neighborsCount = 1 + RNG.Next(MAX_NEIGHBORS_COUNT);
                for (int neighborIndex = 0; neighborIndex < neighborsCount; neighborIndex++)
                {
                    int neighborNodeIndex = RNG.Next(NODES_COUNT - 1);
                    neighborNodeIndex = (nodeIndex + 1 + neighborNodeIndex) % NODES_COUNT;

                    laplacian[(uint)nodeIndex, (uint)neighborNodeIndex] = -1;
                    laplacian[(uint)neighborNodeIndex, (uint)nodeIndex] = -1;
                }

                tentativeEdgesCount += neighborsCount;
            }

            // Retrieve the amount of connections
            int edgesCount     = 0;
            int minConnections = int.MaxValue;
            int maxConnections = -int.MaxValue;

            for (int nodeIndex = 0; nodeIndex < NODES_COUNT; nodeIndex++)
            {
                int degree = 0;
                for (int columnIndex = 0; columnIndex < nodeIndex; columnIndex++)
                {
                    degree += laplacian[(uint)nodeIndex, (uint)columnIndex] != 0.0f ? 1 : 0;
                }
                for (int columnIndex = nodeIndex + 1; columnIndex < NODES_COUNT; columnIndex++)
                {
                    degree += laplacian[(uint)nodeIndex, (uint)columnIndex] != 0.0f ? 1 : 0;
                }

                laplacian[(uint)nodeIndex, (uint)nodeIndex] = degree;

                edgesCount    += degree;
                minConnections = Math.Min(minConnections, degree);
                maxConnections = Math.Max(maxConnections, degree);
            }
            edgesCount >>= 1;                   // All edges are accounted twice...

            //////////////////////////////////////////////////////////////////////////
            // 2] Compute full heat diffusion
            float[][] results_full = ComputeHeatDiffusionFull(laplacian, HEAT_DIFFUSION, TIME_STEP, ITERATIONS_COUNT);

            //////////////////////////////////////////////////////////////////////////
            // 3] Compute iterative diffusion
            float[][] results_Euler = ComputeHeatDiffusionEuler(laplacian, HEAT_DIFFUSION, TIME_STEP, ITERATIONS_COUNT);

            //////////////////////////////////////////////////////////////////////////
            // 4] Compare errors between methods
            float[] errors = new float[ITERATIONS_COUNT];
            float   delta;

            for (uint iterationIndex = 0; iterationIndex < ITERATIONS_COUNT; iterationIndex++)
            {
                float[] temps0 = results_full[iterationIndex];
                float[] temps1 = results_Euler[iterationIndex];

                float sqError = 0.0f;
                for (uint nodeIndex = 0; nodeIndex < NODES_COUNT; nodeIndex++)
                {
                    delta    = temps0[nodeIndex] - temps1[nodeIndex];
                    sqError += delta * delta;
                }
                errors[iterationIndex] = Mathf.Sqrt(sqError);
            }
        }
コード例 #3
0
ファイル: GraphForm.cs プロジェクト: unitycoder/GodComplex
        float[][]       ComputeHeatDiffusionFull(MathSolvers.MatrixF _laplacian, float _diffusionCoefficient, float _timeStep, uint _iterationsCount)
        {
            uint nodesCount = _laplacian.ColumnsCount;

            // 1] Compute eigen vectors using singular value decomposition
            //
            MathSolvers.SVD SVD = new MathSolvers.SVD(_laplacian);
            SVD.Decompose();

            float[,]        eigenVectors = SVD.U.AsArray;
            float[] eigenValues = SVD.w.AsArray;

// Here we make sure U and V are transposed of each other and U is an orthonormal basis
//          float[,]	test = new float[SVD.U.RowsCount,SVD.U.ColumnsCount];
//          float[,]	test2 = new float[SVD.U.RowsCount,SVD.U.ColumnsCount];
//          float[,]	test3 = new float[SVD.U.RowsCount,SVD.U.ColumnsCount];
//          for ( uint r=0; r < SVD.U.RowsCount; r++ ) {
//              for ( uint c=0; c < SVD.U.ColumnsCount; c++ ) {
//
//                  float	sum = 0.0f;
//                  float	sum2 = 0.0f;
//                  for ( uint i=0; i < SVD.U.RowsCount; i++ ) {
//                      sum += SVD.U[r,i] * SVD.V[i,c];
//                      sum2 += SVD.U[r,i] * SVD.V[c,i];
//                  }
//
//                  test[r,c] = sum;
//                  test2[r,c] = sum2;
//                  test3[r,c] = SVD.U[r,c] - SVD.V[r,c];
//              }
//          }

// Check eigen vectors are all orthonormal
//          float[,]	test4 = new float[SVD.U.RowsCount,SVD.U.ColumnsCount];
//          for ( uint r=0; r < SVD.U.RowsCount; r++ ) {
//              for ( uint c=0; c < SVD.U.ColumnsCount; c++ ) {
//
//                  float	sum = 0.0f;
//                  for ( uint i=0; i < SVD.U.RowsCount; i++ ) {
//                      sum += SVD.U[r,i] * SVD.U[c,i];
//                  }
//                  if ( r == c ) {
//                      if ( Mathf.Abs( sum - 1 ) > 1e-4f )
//                          throw new Exception( "Not 1 along diagonal!" );
//                      else
//                          sum = 1;
//                  } else {
//                      if ( Mathf.Abs( sum ) > 1e-4f )
//                          throw new Exception( "Not 0 off diagonal!" );
//                      else
//                          sum = 0;
//                  }
//                  test4[r,c] = sum;
//              }
//          }

// Check recomposed matrix equals original matrix
//			float[,]	test5 = new float[SVD.U.RowsCount,SVD.U.ColumnsCount];
//          for ( uint r=0; r < SVD.U.RowsCount; r++ ) {
//              for ( uint c=0; c < SVD.U.ColumnsCount; c++ ) {
//
//                  float	sum = 0.0f;
//                  for ( uint i=0; i < SVD.U.RowsCount; i++ ) {
//                      sum += eigenVectors[r,i] * eigenValues[i] * eigenVectors[c,i];
//                  }
//                  sum -= laplacian[r,c];
//                  test5[r,c] = sum;
//              }
//          }


            // 2] Apply heat to node 0 and compute diffusion
            //
            float[][] results = new float[_iterationsCount][];

            float[] heatsSource      = new float[nodesCount];
            float[] eigenHeatsSource = new float[nodesCount];
            float[] eigenHeatsTarget = new float[nodesCount];

            heatsSource[0] = 1;

            // 3.1) Transform heat vector into eigen-space: Phi' = trans(V) * Phi
            for (int i = 0; i < nodesCount; i++)
            {
                float sum = 0.0f;
                for (int j = 0; j < nodesCount; j++)
                {
                    sum += eigenVectors[j, i] * heatsSource[j];
                }
                eigenHeatsSource[i] = sum;
            }

            // 3.2) Apply diffusion iteratively (note that we could obviously reach the desired time immediately,
            //		there's no use for iterative computation here but it's only there to compare against Euler integration)
            //
            for (uint iterationIndex = 0; iterationIndex < _iterationsCount; iterationIndex++)
            {
                float time = _timeStep * (iterationIndex + 1);
                for (int i = 0; i < nodesCount; i++)
                {
                    float lambda = eigenValues[i];
                    float phi_0  = eigenHeatsSource[i];
                    float phi_t  = phi_0 * Mathf.Exp(-_diffusionCoefficient * lambda * time);
                    eigenHeatsTarget[i] = phi_t;
                }

                // Transform eigen-heat vector back into graph-space: Phi = V * Phi'
                float[] heatsTarget = new float[nodesCount];
                float   totalHeat   = 0.0f;                     // This should equal to initial heat, no loss!
                for (int i = 0; i < nodesCount; i++)
                {
                    float sum = 0.0f;
                    for (int j = 0; j < nodesCount; j++)
                    {
                        sum += eigenVectors[i, j] * eigenHeatsTarget[j];
                    }
                    heatsTarget[i] = sum;
                    totalHeat     += sum;
                }

                // Copy each iteration
                results[iterationIndex] = heatsTarget;
            }

            return(results);
        }
コード例 #4
0
ファイル: GraphForm.cs プロジェクト: unitycoder/GodComplex
        void    BuildGraph()
        {
            // Build random obstacles
            const int OBSTACLES_COUNT = 1000;

            uint[,]         obstaclesMap = new uint[GRAPH_SIZE, GRAPH_SIZE];
            Random RNG = new Random(1);
            int    X, Y;

            for (int i = 0; i < OBSTACLES_COUNT; i++)
            {
                X = RNG.Next(GRAPH_SIZE);
                Y = RNG.Next(GRAPH_SIZE);
                obstaclesMap[X, Y] = ~0U;
            }

            // Count valid nodes
            uint nodesCount = 0;                // Must equal at least GRAPH_SIZE*GRAPH_SIZE - OBSTACLES_COUNT when exiting

            for (Y = 0; Y < GRAPH_SIZE; Y++)
            {
                for (X = 0; X < GRAPH_SIZE; X++)
                {
                    if (obstaclesMap[X, Y] == ~0U)
                    {
                        continue;
                    }

                    obstaclesMap[X, Y] = nodesCount++;
                }
            }

            // Build the Laplacian matrix
            MathSolvers.MatrixF laplacianMatrix = new MathSolvers.MatrixF(nodesCount, nodesCount);
//			float[,]	laplacianMatrix = new float[nodesCount,nodesCount];
            int[,]          dXY = new int[8, 2] {
                { -1, -1 },
                { -1, 0 },
                { -1, +1 },
                { 0, +1 },
                { +1, +1 },
                { +1, 0 },
                { +1, -1 },
                { 0, -1 },
            };

            X = 0;
            Y = 0;
            for (int nodeIndex = 0; nodeIndex < nodesCount; nodeIndex++)
            {
                // Skip obstacles
                while (obstaclesMap[X, Y] == ~0U)
                {
                    X++;
                    if (X >= GRAPH_SIZE)
                    {
                        X = 0;
                        Y++;
                    }
                }

                // Collect connections
                int degree = 0;
                for (int neighborIndex = 0; neighborIndex < 8; neighborIndex++)
                {
                    int Nx = X + dXY[neighborIndex, 0];
                    int Ny = Y + dXY[neighborIndex, 1];
                    if (Nx < 0 || Nx >= GRAPH_SIZE || Ny < 0 || Ny >= GRAPH_SIZE)
                    {
                        continue;                               // Out of graph...
                    }
                    uint neighborNodeIndex = obstaclesMap[Nx, Ny];
                    if (neighborNodeIndex == ~0U)
                    {
                        continue;                               // Obstacle...
                    }
                    laplacianMatrix[(uint)nodeIndex, (uint)neighborNodeIndex] = -1;
                    laplacianMatrix[(uint)neighborNodeIndex, (uint)nodeIndex] = -1;
                    degree++;
                }

                laplacianMatrix[(uint)nodeIndex, (uint)nodeIndex] = degree;                     // At most 8
            }

//          //////////////////////////////////////////////////////////////////////////
//          // Compute eigen vectors using singular value decomposition
//          //
//          MathSolvers.SVD		SVD = new MathSolvers.SVD( laplacianMatrix );
//          SVD.Decompose();
//
//          MathSolvers.MatrixF	test = new MathSolvers.MatrixF( SVD.U.RowsCount, SVD.U.ColumnsCount );
//          for ( uint r=0; r < test.RowsCount; r++ ) {
//              for ( uint c=0; c < test.ColumnsCount; c++ ) {
//
//                  float	sum = 0.0f;
//                  for ( uint i=0; i < test.RowsCount; i++ ) {
//                      sum += SVD.U[r,i] * SVD.V[i,c];
//                  }
//              }
//          }
        }