示例#1
0
 public mcpdstate(mcpd.mcpdstate obj)
 {
     _innerobj = obj;
 }
示例#2
0
        //
        // Public declarations
        //

        public mcpdstate()
        {
            _innerobj = new mcpd.mcpdstate();
        }
        /*************************************************************************
        Test bound constraints.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testlc(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] c = new double[0,0];
            double[,] xy = new double[0,0];
            int[] ct = new int[0];
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            int t = 0;
            int jc = 0;
            double v = 0;
            double threshold = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();

            threshold = 1.0E5*math.machineepsilon;
            
            //
            // We try different problems with following properties:
            // * N is large enough - we won't have problems with inconsistent constraints
            // * first state is either "entry" or "normal"
            // * last state is either "exit" or "normal"
            // * we have one long random track
            //
            // We test several properties which are described in comments below
            //
            for(n=4; n<=6; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        
                        //
                        // Prepare problem
                        //
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = 0;
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = n-1;
                        }
                        xy = new double[2*n, n];
                        for(i=0; i<=ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=ap.cols(xy)-1; j++)
                            {
                                xy[i,j] = math.randomreal();
                            }
                        }
                        
                        //
                        // Test that single linear equality/inequality constraint
                        // on non-entry non-exit elements of P is satisfied.
                        //
                        // NOTE 1: this test needs N>=4 because smaller values
                        //         can give us inconsistent constraints
                        // NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
                        //         is always feasible. It guarantees that there always exists
                        //         at least one feasible point
                        // NOTE 3: If we have inequality constraint, we "shift" right part
                        //         in order to make feasible some neighborhood of P=(1/N ... 1/N).
                        //
                        ap.assert(n>=4, "TestLC: expectation failed");
                        c = new double[1, n*n+1];
                        ct = new int[1];
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( ((i==0 | i==n-1) | j==0) | j==n-1 )
                                {
                                    c[0,i*n+j] = 0;
                                }
                                else
                                {
                                    c[0,i*n+j] = math.randomreal();
                                    v = v+c[0,i*n+j]*((double)1/(double)n);
                                }
                            }
                        }
                        c[0,n*n] = v;
                        ct[0] = math.randominteger(3)-1;
                        if( ct[0]<0 )
                        {
                            c[0,n*n] = c[0,n*n]+0.1;
                        }
                        if( ct[0]>0 )
                        {
                            c[0,n*n] = c[0,n*n]-0.1;
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            v = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+p[i,j]*c[0,i*n+j];
                                }
                            }
                            if( ct[0]<0 )
                            {
                                err = err | (double)(v)>=(double)(c[0,n*n]+threshold);
                            }
                            if( ct[0]==0 )
                            {
                                err = err | (double)(Math.Abs(v-c[0,n*n]))>=(double)(threshold);
                            }
                            if( ct[0]>0 )
                            {
                                err = err | (double)(v)<=(double)(c[0,n*n]-threshold);
                            }
                        }
                        else
                        {
                            err = true;
                        }
                        
                        //
                        // Test interaction with default "sum-to-one" constraint
                        // on columns of P.
                        //
                        // We set linear constraint which has for "sum-to-X" on
                        // on random non-exit column of P. This constraint can be
                        // either consistent (X=1.0) or inconsistent (X<>1.0) with
                        // this default constraint.
                        //
                        // Algorithm must detect inconsistency.
                        //
                        // NOTE:
                        // 1. this test needs N>=2
                        //
                        ap.assert(n>=2, "TestLC: expectation failed");
                        jc = math.randominteger(n-1);
                        c = new double[1, n*n+1];
                        ct = new int[1];
                        for(i=0; i<=n*n-1; i++)
                        {
                            c[0,i] = 0.0;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            c[0,n*i+jc] = 1.0;
                        }
                        c[0,n*n] = 1.0;
                        ct[0] = 0;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype<=0;
                        c[0,n*n] = 2.0;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetlc(s, c, ct, 1);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype!=-3;
                        
                        //
                        // Test interaction with constrains on entry states.
                        //
                        // When model has entry state, corresponding row of P
                        // must be zero. We try to set two kinds of constraints
                        // on elements of this row:
                        // * sums-to-zero constraint, which must be consistent
                        // * sums-to-one constraint, which must be inconsistent
                        //
                        if( entrystate>=0 )
                        {
                            c = new double[1, n*n+1];
                            ct = new int[1];
                            for(i=0; i<=n*n-1; i++)
                            {
                                c[0,i] = 0.0;
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                c[0,n*entrystate+j] = 1.0;
                            }
                            ct[0] = 0;
                            c[0,n*n] = 0.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            c[0,n*n] = 1.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test interaction with constrains on exit states.
                        //
                        // When model has exit state, corresponding column of P
                        // must be zero. We try to set two kinds of constraints
                        // on elements of this column:
                        // * sums-to-zero constraint, which must be consistent
                        // * sums-to-one constraint, which must be inconsistent
                        //
                        if( exitstate>=0 )
                        {
                            c = new double[1, n*n+1];
                            ct = new int[1];
                            for(i=0; i<=n*n-1; i++)
                            {
                                c[0,i] = 0.0;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                c[0,n*i+exitstate] = 1.0;
                            }
                            ct[0] = 0;
                            c[0,n*n] = 0.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            c[0,n*n] = 1.0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdsetlc(s, c, ct, 1);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                    }
                }
            }
            
            //
            // Final test - we generate several random constraints and
            // test SetLC() function.
            //
            // NOTES:
            //
            // 1. Constraints are generated is such a way that P=(1/N ... 1/N)
            //    is always feasible. It guarantees that there always exists
            //    at least one feasible point
            // 2. For simplicity of the test we do not use entry/exit states
            //    in our model
            //
            for(n=1; n<=4; n++)
            {
                for(k=1; k<=2*n; k++)
                {
                    
                    //
                    // Generate track
                    //
                    xy = new double[2*n, n];
                    for(i=0; i<=ap.rows(xy)-1; i++)
                    {
                        for(j=0; j<=ap.cols(xy)-1; j++)
                        {
                            xy[i,j] = math.randomreal();
                        }
                    }
                    
                    //
                    // Generate random constraints
                    //
                    c = new double[k, n*n+1];
                    ct = new int[k];
                    for(i=0; i<=k-1; i++)
                    {
                        
                        //
                        // Generate constraint and its right part
                        //
                        c[i,n*n] = 0;
                        for(j=0; j<=n*n-1; j++)
                        {
                            c[i,j] = 2*math.randomreal()-1;
                            c[i,n*n] = c[i,n*n]+c[i,j]*((double)1/(double)n);
                        }
                        ct[i] = math.randominteger(3)-1;
                        
                        //
                        // If we have inequality constraint, we "shift" right part
                        // in order to make feasible some neighborhood of P=(1/N ... 1/N).
                        //
                        if( ct[i]<0 )
                        {
                            c[i,n*n] = c[i,n*n]+0.1;
                        }
                        if( ct[i]>0 )
                        {
                            c[i,n*n] = c[i,n*n]-0.1;
                        }
                    }
                    
                    //
                    // Test
                    //
                    createee(n, -1, -1, s);
                    mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                    mcpd.mcpdsetlc(s, c, ct, k);
                    mcpd.mcpdsolve(s);
                    mcpd.mcpdresults(s, ref p, rep);
                    if( rep.terminationtype>0 )
                    {
                        for(t=0; t<=k-1; t++)
                        {
                            v = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+p[i,j]*c[t,i*n+j];
                                }
                            }
                            if( ct[t]<0 )
                            {
                                err = err | (double)(v)>=(double)(c[t,n*n]+threshold);
                            }
                            if( ct[t]==0 )
                            {
                                err = err | (double)(Math.Abs(v-c[t,n*n]))>=(double)(threshold);
                            }
                            if( ct[t]>0 )
                            {
                                err = err | (double)(v)<=(double)(c[t,n*n]-threshold);
                            }
                        }
                    }
                    else
                    {
                        err = true;
                    }
                }
            }
        }
        /*************************************************************************
        Test bound constraints.

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testbc(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] bndl = new double[0,0];
            double[,] bndu = new double[0,0];
            double[,] xy = new double[0,0];
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int i = 0;
            int j = 0;
            int ic = 0;
            int jc = 0;
            double vl = 0;
            double vu = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();

            
            //
            // We try different problems with following properties:
            // * N is large enough - we won't have problems with inconsistent constraints
            // * first state is either "entry" or "normal"
            // * last state is either "exit" or "normal"
            // * we have one long random track
            //
            // We test several properties which are described in comments below
            //
            for(n=4; n<=6; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        
                        //
                        // Prepare problem
                        //
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = 0;
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = n-1;
                        }
                        xy = new double[2*n, n];
                        for(i=0; i<=ap.rows(xy)-1; i++)
                        {
                            for(j=0; j<=ap.cols(xy)-1; j++)
                            {
                                xy[i,j] = math.randomreal();
                            }
                        }
                        
                        //
                        // Test that single bound constraint on non-entry
                        // non-exit elements of P is satisfied.
                        //
                        // NOTE 1: this test needs N>=4 because smaller values
                        // can give us inconsistent constraints
                        //
                        ap.assert(n>=4, "TestBC: expectation failed");
                        ic = 1+math.randominteger(n-2);
                        jc = 1+math.randominteger(n-2);
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            vl = 0.3*math.randomreal();
                        }
                        else
                        {
                            vl = Double.NegativeInfinity;
                        }
                        if( (double)(math.randomreal())>(double)(0.5) )
                        {
                            vu = 0.5+0.3*math.randomreal();
                        }
                        else
                        {
                            vu = Double.PositiveInfinity;
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdaddbc(s, ic, jc, vl, vu);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            err = err | (double)(p[ic,jc])<(double)(vl);
                            err = err | (double)(p[ic,jc])>(double)(vu);
                        }
                        else
                        {
                            err = true;
                        }
                        
                        //
                        // Test interaction with default "sum-to-one" constraint
                        // on columns of P.
                        //
                        // We set N-1 bound constraints on random non-exit column
                        // of P, which are inconsistent with this default constraint
                        // (sum will be greater that 1.0).
                        //
                        // Algorithm must detect inconsistency.
                        //
                        // NOTE:
                        // 1. we do not set constraints for the first element of
                        //    the column, because this element may be constrained by
                        //    "exit state" constraint.
                        // 2. this test needs N>=3
                        //
                        ap.assert(n>=3, "TestEC: expectation failed");
                        jc = math.randominteger(n-1);
                        vl = 0.85;
                        vu = 0.95;
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        for(i=1; i<=n-1; i++)
                        {
                            mcpd.mcpdaddbc(s, i, jc, vl, vu);
                        }
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        err = err | rep.terminationtype!=-3;
                        
                        //
                        // Test interaction with constrains on entry states.
                        //
                        // When model has entry state, corresponding row of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this row:
                        // * bound constraint with zero lower bound, which must be consistent
                        // * bound constraint with non-zero lower bound, which must be inconsistent
                        //
                        if( entrystate>=0 )
                        {
                            jc = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, entrystate, jc, 0.0, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, entrystate, jc, 0.5, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test interaction with constrains on exit states.
                        //
                        // When model has exit state, corresponding column of P
                        // must be zero. We try to set two kinds of constraints
                        // on random element of this column:
                        // * bound constraint with zero lower bound, which must be consistent
                        // * bound constraint with non-zero lower bound, which must be inconsistent
                        //
                        if( exitstate>=0 )
                        {
                            ic = math.randominteger(n);
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, ic, exitstate, 0.0, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype<=0;
                            createee(n, entrystate, exitstate, s);
                            mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            mcpd.mcpdaddbc(s, ic, exitstate, 0.5, 1.0);
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            err = err | rep.terminationtype!=-3;
                        }
                        
                        //
                        // Test SetBC() call - we constrain subset of non-entry
                        // non-exit elements and test it.
                        //
                        ap.assert(n>=4, "TestBC: expectation failed");
                        bndl = new double[n, n];
                        bndu = new double[n, n];
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                bndl[i,j] = Double.NegativeInfinity;
                                bndu[i,j] = Double.PositiveInfinity;
                            }
                        }
                        for(j=1; j<=n-2; j++)
                        {
                            i = 1+math.randominteger(n-2);
                            bndl[i,j] = 0.5-0.1*math.randomreal();
                            bndu[i,j] = 0.5+0.1*math.randomreal();
                        }
                        createee(n, entrystate, exitstate, s);
                        mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                        mcpd.mcpdsetbc(s, bndl, bndu);
                        mcpd.mcpdsolve(s);
                        mcpd.mcpdresults(s, ref p, rep);
                        if( rep.terminationtype>0 )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    err = err | (double)(p[i,j])<(double)(bndl[i,j]);
                                    err = err | (double)(p[i,j])>(double)(bndu[i,j]);
                                }
                            }
                        }
                        else
                        {
                            err = true;
                        }
                    }
                }
            }
        }
        /*************************************************************************
        Test for different combinations of "entry"/"exit" models

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testentryexit(ref bool err)
        {
            int n = 0;
            double[,] p = new double[0,0];
            double[,] pexact = new double[0,0];
            double[,] xy = new double[0,0];
            double threshold = 0;
            int entrystate = 0;
            int exitstate = 0;
            int entrykind = 0;
            int exitkind = 0;
            int popkind = 0;
            int i = 0;
            int j = 0;
            int k = 0;
            double v = 0;
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();
            int i_ = 0;

            threshold = 1.0E-3;
            
            //
            //
            //
            for(n=2; n<=5; n++)
            {
                for(entrykind=0; entrykind<=1; entrykind++)
                {
                    for(exitkind=0; exitkind<=1; exitkind++)
                    {
                        for(popkind=0; popkind<=1; popkind++)
                        {
                            
                            //
                            // Generate EntryState/ExitState such that one of the following is True:
                            // * EntryState<>ExitState
                            // * EntryState=-1 or ExitState=-1
                            //
                            do
                            {
                                if( entrykind==0 )
                                {
                                    entrystate = -1;
                                }
                                else
                                {
                                    entrystate = math.randominteger(n);
                                }
                                if( exitkind==0 )
                                {
                                    exitstate = -1;
                                }
                                else
                                {
                                    exitstate = math.randominteger(n);
                                }
                            }
                            while( !((entrystate==-1 | exitstate==-1) | entrystate!=exitstate) );
                            
                            //
                            // Generate transition matrix P such that:
                            // * columns corresponding to non-exit states sums to 1.0
                            // * columns corresponding to exit states sums to 0.0
                            // * rows corresponding to entry states are zero
                            //
                            pexact = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    pexact[i,j] = 1+math.randominteger(5);
                                    if( i==entrystate )
                                    {
                                        pexact[i,j] = 0.0;
                                    }
                                    if( j==exitstate )
                                    {
                                        pexact[i,j] = 0.0;
                                    }
                                }
                            }
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i=0; i<=n-1; i++)
                                {
                                    v = v+pexact[i,j];
                                }
                                if( (double)(v)!=(double)(0) )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        pexact[i,j] = pexact[i,j]/v;
                                    }
                                }
                            }
                            
                            //
                            // Create MCPD solver
                            //
                            if( entrystate<0 & exitstate<0 )
                            {
                                mcpd.mcpdcreate(n, s);
                            }
                            if( entrystate>=0 & exitstate<0 )
                            {
                                mcpd.mcpdcreateentry(n, entrystate, s);
                            }
                            if( entrystate<0 & exitstate>=0 )
                            {
                                mcpd.mcpdcreateexit(n, exitstate, s);
                            }
                            if( entrystate>=0 & exitstate>=0 )
                            {
                                mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s);
                            }
                            
                            //
                            // Add N tracks.
                            //
                            // K-th track starts from vector with large value of
                            // K-th component and small random noise in other components.
                            //
                            // Track contains from 2 to 4 elements.
                            //
                            // Tracks contain proportional (normalized) or
                            // population data, depending on PopKind variable.
                            //
                            for(k=0; k<=n-1; k++)
                            {
                                
                                //
                                // Generate track whose length is in 2..4
                                //
                                xy = new double[2+math.randominteger(3), n];
                                for(j=0; j<=n-1; j++)
                                {
                                    xy[0,j] = 0.05*math.randomreal();
                                }
                                xy[0,k] = 1+math.randomreal();
                                for(i=1; i<=ap.rows(xy)-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        if( j!=entrystate )
                                        {
                                            v = 0.0;
                                            for(i_=0; i_<=n-1;i_++)
                                            {
                                                v += pexact[j,i_]*xy[i-1,i_];
                                            }
                                            xy[i,j] = v;
                                        }
                                        else
                                        {
                                            xy[i,j] = math.randomreal();
                                        }
                                    }
                                }
                                
                                //
                                // Normalize, if needed
                                //
                                if( popkind==1 )
                                {
                                    for(i=0; i<=ap.rows(xy)-1; i++)
                                    {
                                        v = 0.0;
                                        for(j=0; j<=n-1; j++)
                                        {
                                            v = v+xy[i,j];
                                        }
                                        if( (double)(v)>(double)(0) )
                                        {
                                            for(j=0; j<=n-1; j++)
                                            {
                                                xy[i,j] = xy[i,j]/v;
                                            }
                                        }
                                    }
                                }
                                
                                //
                                // Add track
                                //
                                mcpd.mcpdaddtrack(s, xy, ap.rows(xy));
                            }
                            
                            //
                            // Solve and test
                            //
                            mcpd.mcpdsolve(s);
                            mcpd.mcpdresults(s, ref p, rep);
                            if( rep.terminationtype>0 )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                                    }
                                }
                            }
                            else
                            {
                                err = true;
                            }
                        }
                    }
                }
            }
        }
        /*************************************************************************
        Simple test with no "entry"/"exit" states

        On failure sets Err to True (leaves it unchanged otherwise)
        *************************************************************************/
        private static void testsimple(ref bool err)
        {
            int n = 0;
            double[,] pexact = new double[0,0];
            double[,] xy = new double[0,0];
            double threshold = 0;
            int i = 0;
            int j = 0;
            double v = 0;
            double v0 = 0;
            double[,] p = new double[0,0];
            mcpd.mcpdstate s = new mcpd.mcpdstate();
            mcpd.mcpdreport rep = new mcpd.mcpdreport();
            double offdiagonal = 0;

            threshold = 1.0E-2;
            
            //
            // First test:
            // * N-dimensional problem
            // * proportional data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
            // * all tracks are modelled using randomly generated transition matrix P
            //
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    xy[0,i] = 1;
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = pexact[j,i];
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
            
            //
            // Second test:
            // * N-dimensional problem
            // * proportional data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
            // * all tracks are modelled using randomly generated transition matrix P
            //
            offdiagonal = 0.1;
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    
                    //
                    // "main" element
                    //
                    xy[0,i] = 1.0-2*offdiagonal;
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = (1.0-2*offdiagonal)*pexact[j,i];
                    }
                    
                    //
                    // off-diagonal ones
                    //
                    if( i>0 )
                    {
                        xy[0,i-1] = offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+offdiagonal*pexact[j,i-1];
                        }
                    }
                    if( i<n-1 )
                    {
                        xy[0,i+1] = offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+offdiagonal*pexact[j,i+1];
                        }
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
            
            //
            // Third test:
            // * N-dimensional problem
            // * population data
            // * no "entry"/"exit" states
            // * N tracks, each includes only two states
            // * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position, V in [1,10]
            // * all tracks are modelled using randomly generated transition matrix P
            //
            offdiagonal = 0.1;
            for(n=1; n<=5; n++)
            {
                
                //
                // Initialize "exact" P:
                // * fill by random values
                // * make sure that each column sums to non-zero value
                // * normalize
                //
                pexact = new double[n, n];
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        pexact[i,j] = math.randomreal();
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    i = math.randominteger(n);
                    pexact[i,j] = pexact[i,j]+0.1;
                }
                for(j=0; j<=n-1; j++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+pexact[i,j];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        pexact[i,j] = pexact[i,j]/v;
                    }
                }
                
                //
                // Initialize solver:
                // * create object
                // * add tracks
                //
                mcpd.mcpdcreate(n, s);
                for(i=0; i<=n-1; i++)
                {
                    xy = new double[2, n];
                    for(j=0; j<=n-1; j++)
                    {
                        xy[0,j] = 0;
                    }
                    
                    //
                    // "main" element
                    //
                    v0 = 9*math.randomreal()+1;
                    xy[0,i] = v0*(1.0-2*offdiagonal);
                    for(j=0; j<=n-1; j++)
                    {
                        xy[1,j] = v0*(1.0-2*offdiagonal)*pexact[j,i];
                    }
                    
                    //
                    // off-diagonal ones
                    //
                    if( i>0 )
                    {
                        xy[0,i-1] = v0*offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+v0*offdiagonal*pexact[j,i-1];
                        }
                    }
                    if( i<n-1 )
                    {
                        xy[0,i+1] = v0*offdiagonal;
                        for(j=0; j<=n-1; j++)
                        {
                            xy[1,j] = xy[1,j]+v0*offdiagonal*pexact[j,i+1];
                        }
                    }
                    mcpd.mcpdaddtrack(s, xy, 2);
                }
                
                //
                // Solve and test
                //
                mcpd.mcpdsolve(s);
                mcpd.mcpdresults(s, ref p, rep);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            err = err | (double)(Math.Abs(p[i,j]-pexact[i,j]))>(double)(threshold);
                        }
                    }
                }
                else
                {
                    err = true;
                }
            }
        }