def heat_equation_study():
    from examples.models.heat_equation.heat_equation import \
        SteadyStateHeatEquation1D
    heat_model = SteadyStateHeatEquation1D()
    
    g = lambda x: heat_model( x ).squeeze()

    num_dims = 10
    f = CppModel( g )
    heat_model.num_dims = num_dims

    func_basename = 'hetrog-heateq-'
    data_path = '/home/jdjakem/software/pyheat/work/gp-pce-comparison/data'
    data_path = join(data_path, 'convergence-data' )
    test_pts_filename = join( data_path, "TestPts" + str( num_dims )+
                              ".txt" )

    test_vals_filename = join( join( data_path, 'heat-equation'  ), 
                               func_basename + 
                               str( num_dims )+
                               '-test-values.txt' )
    test_pts = numpy.loadtxt( test_pts_filename, delimiter = ',' ).T
    domain =  set_hypercube_domain( num_dims, -1., 1.)
    unit_hypercube = set_hypercube_domain( num_dims, 0., 1.)
    test_pts = hypercube_map( test_pts, unit_hypercube, domain )
    test_vals = numpy.loadtxt( test_vals_filename, 
                               delimiter = ' ' ).squeeze()

    #quadrature_rule_1d = ClenshawCurtisQuadRule1D()
    quadrature_rule_1d = GaussPattersonQuadRule1D()
    basis = LagrangePolynomialBasis()
    
    max_level = 30
    SG, error, num_pts = build_sparse_grid_cpp( quadrature_rule_1d, basis,
                                                domain = domain, 
                                                f = f, num_dims = num_dims, 
                                                max_level = max_level,
                                                max_num_points = 100000,
                                                test_pts = test_pts, 
                                                test_vals = test_vals )
    SG.num_points()

    import pylab
    pylab.loglog( num_pts, error, 'o-' )
    pylab.show()

    print 'test_mean', test_vals.mean()
def osciallator_study():

    oscillator_model = RandomOscillator()
    g = lambda x: oscillator_model( x ).squeeze()
    f = CppModel( g )
    num_dims = 6
    domain =  numpy.array( [0.08,0.12,0.03,0.04,0.08,0.12,
                            0.8,1.2,0.45,0.55,-0.05,0.05], numpy.double )

    ##---------------------------------------------------------------------
    # Read in test data
    ##---------------------------------------------------------------------
    """
    func_basename = 'oscillator-'
    data_path = '/home/jdjakem/software/pyheat/work/gp-pce-comparison/data'
    data_path = join(data_path, 'convergence-data' )
    test_pts_filename = join( data_path, "TestPts" + str( num_dims )+
    ".txt" )
    test_pts = numpy.loadtxt( test_pts_filename, delimiter = ' ' ).T    
    test_pts = domain.map_from_unit_hypercube( test_pts )
    test_vals_filename = join( join( data_path, 'random-oscillator'  ), 
    func_basename + 
    str( num_dims )+
    '-test-values.txt' )
    test_vals = numpy.loadtxt( test_vals_filename, 
    delimiter = ' ' )
    """
    test_pts = numpy.random.uniform( 0, 1, (num_dims, 10000) )
    unit_hypercube = set_hypercube_domain( num_dims, 0., 1. )
    test_pts = hypercube_map( test_pts, unit_hypercube, domain )
    test_vals = f.evaluate_set(test_pts).squeeze()

    
    ##---------------------------------------------------------------------
    # Build sparse grid to machine precision
    ##--------------------------------------------------------------------- 
    print 'Building sparse grid'
    quadrature_rule_1d = ClenshawCurtisQuadRule1D()
    #quadrature_rule_1d = GaussPattersonQuadRule1D()
    basis = LagrangePolynomialBasis()
    max_level = 20
    SG, sg_error, num_pts = build_sparse_grid_cpp( quadrature_rule_1d, basis,
                                                   domain = domain, 
                                                   f = f, num_dims = num_dims, 
                                                   max_level = max_level,
                                                   max_num_points = 1000,
                                                   test_pts = test_pts, 
                                                   test_vals = test_vals,
                                                   breaks = None )
    
    print 'num points in sparse grid', SG.num_points()
    pred_vals = SG.evaluate_set( test_pts ).squeeze()
    print SG.num_function_evaluations()
    print 'test_mean', test_vals.mean()
    print 'grid mean', pred_vals.mean()
    print 'sparse grid error: ', get_l2_error( test_vals, pred_vals )

    
    ##---------------------------------------------------------------------
    # Convert the sparse grid into a PCE
    ##--------------------------------------------------------------------- 
    print 'Building PCE'
    # test conversion to pce
    poly_1d = OrthogPolyVector()
    poly_1d.resize( 1 )
    poly_1d[0] = LegendrePolynomial1D()
    basis = TensorProductBasis()
    basis.set_domain( domain )
    basis.set_bases( poly_1d, 
                     [numpy.arange( num_dims, dtype=numpy.int32 )], 
                     num_dims )
    pce = PolynomialChaosExpansion()
    pce.domain( domain )
    pce.basis( basis )
    SG.convert_to_polynomial_chaos_expansion( pce )
    print 'PCE error: ', get_l2_error( test_vals, 
                                       pce.evaluate_set( test_pts ).squeeze() )

    oracle_basis = PolyIndexVector()
    pce.get_basis_indices( oracle_basis )

    ##---------------------------------------------------------------------
    # Initialise PCEs
    ##---------------------------------------------------------------------
    pce_least = PolynomialChaosExpansion()
    pce_least.domain( domain )
    pce_least.basis( basis )

    pce_least_oracle = PolynomialChaosExpansion()
    pce_least_oracle.domain( domain )
    pce_least_oracle.basis( basis )

    pce_omp = PolynomialChaosExpansion()
    pce_omp.domain( domain )
    pce_omp.basis( basis )

    pce_omp_oracle = PolynomialChaosExpansion()
    pce_omp_oracle.domain( domain )
    pce_omp_oracle.basis( basis )

    pce_nesta = PolynomialChaosExpansion()
    pce_nesta.domain( domain )
    pce_nesta.basis( basis )

    pce_nesta_oracle = PolynomialChaosExpansion()
    pce_nesta_oracle.domain( domain )
    pce_nesta_oracle.basis( basis )

    pce_spgl1 = PolynomialChaosExpansion()
    pce_spgl1.domain( domain )
    pce_spgl1.basis( basis )

    pce_lsq_oracle = PolynomialChaosExpansion()
    pce_lsq_oracle.domain( domain )
    pce_lsq_oracle.basis( basis )

    sovler_basenames = ['lsq-oracle', 'least-interpolant', 'nesta', 
                        'nesta_oracle', 'omp', 'omp-oracle']

    ##---------------------------------------------------------------------
    # Pre compute information necessary for best N term PCE
    ##---------------------------------------------------------------------
    coeff = pce.get_coefficients()
    basis_indices = PolyIndexVector()
    pce.get_basis_indices( basis_indices )
    indices = sorted( basis_indices, 
                      key = lambda x: x.get_array_index() )

    
    ##---------------------------------------------------------------------
    # Perfrom convergence study
    ##---------------------------------------------------------------------

    num_pts_sg = num_pts
    max_num_points = 200
    num_pts = numpy.logspace( numpy.log10(10*num_dims), numpy.log10( max_num_points ), 5 )
    num_pts = numpy.asarray( num_pts, dtype = numpy.int32 )

    nterm_pce_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    least_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    least_oracle_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    omp_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    omp_oracle_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    nesta_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    nesta_oracle_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    spgl1_error = numpy.empty( ( len( num_pts ) ), numpy.double )
    lsq_oracle_error = numpy.empty( ( len( num_pts ) ), numpy.double )


    I = numpy.argsort( numpy.absolute( coeff[:,0]**2 ) )[::-1]
    for n in xrange( len( num_pts ) ):
        #
        # Construct best N term approximation
        #
        num_indices = 0
        pce.set_coefficients( coeff[I[:num_pts[n]]].reshape( ( num_pts[n], 1 ) ))
        N_term_basis_indices = PolyIndexVector()
        N_term_basis_indices.resize( int(num_pts[n]) )
        max_degree = 0
        for i in I[:num_pts[n]]:
            N_term_basis_indices[num_indices] = ( indices[i] )
            indices[i].set_array_index( num_indices )
            num_indices += 1
            max_degree = max( max_degree, indices[i].level_sum() )
            #print indices[i]
        print 'max_degree', max_degree, num_pts[n]
        pce.set_basis_indices( N_term_basis_indices )
        pred_vals = pce.evaluate_set( test_pts ).squeeze()
        l2_error = get_l2_error( test_vals, pred_vals )

        print 'best N term pce error: ', l2_error
        nterm_pce_error[n] = l2_error
        
        #
        # Construct PCE on a lHD and repeat to account for statistical variation
        # in the LHD
        #
        from math_tools_cpp import ilhs, lhs
        seed = 1
        num_reps = 10
        least_errors = numpy.ones( num_reps )
        least_oracle_errors = numpy.ones( num_reps )
        omp_errors = numpy.ones( num_reps )
        omp_oracle_errors = numpy.ones( num_reps )
        nesta_errors = numpy.ones( num_reps )
        nesta_oracle_errors = numpy.ones( num_reps )
        spgl1_errors = numpy.ones( num_reps )
        lsq_oracle_errors = numpy.ones( num_reps )
        for k in xrange( num_reps ):
            build_pts = lhs( num_dims, num_pts[n], seed )
            seed += 1
            #build_pts = ilhs( num_dims, num_pts[n], 2, 0 );
            build_pts = hypercube_map( build_pts, unit_hypercube, domain )
            build_vals = f.evaluate_set( build_pts )
            """
            # least interpolant
            pce_least.set_basis_indices( PolyIndexVector() )
            least_interpolation( build_pts, 
                                 build_vals.reshape( build_vals.shape[0], 1 ), 
                                 pce_least );
            pred_vals = pce_least.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            least_errors[k] = l2_error
            print 'least interpolant ', l2_error
           
            # oracle least interpolant
            pce_least_oracle.set_basis_indices( N_term_basis_indices )
            least_interpolation( build_pts, 
            build_vals.reshape( build_vals.shape[0], 1 ), 
            pce_least_oracle );
            pred_vals = pce_least_oracle.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            least_oracle_errors[k] = l2_error
            
            
            # NESTA pce
            pce_nesta.set_basis_indices( oracle_basis )
            NESTA( build_pts, 
                   build_vals.reshape( build_vals.shape[0], 1 ), 
                   pce_nesta );
            pred_vals = pce_nesta.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            nesta_errors[k] = l2_error
            print 'nesta ', l2_error

            # NESTA oracle pce
            pce_nesta_oracle.set_basis_indices( N_term_basis_indices )
            NESTA( build_pts, 
                   build_vals.reshape( build_vals.shape[0], 1 ), 
                   pce_nesta_oracle );
            pred_vals = pce_nesta_oracle.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            nesta_oracle_errors[k] = l2_error
            print 'nesta oracle', l2_error
            
            # SPGL1 pce
            #pce_spgl1.set_basis_indices( N_term_basis_indices )
            pce_spgl1.set_basis_indices( oracle_basis )
            SPGL1( build_pts, 
            build_vals.reshape( build_vals.shape[0], 1 ), 
            pce_spgl1, test_pts, test_vals );
            pred_vals = pce_spgl1.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            spgl1_errors[k] = l2_error
            print 'spgl1', l2_error
            
            
            # least squares orcale pce
            pce_lsq_oracle.set_basis_indices( N_term_basis_indices )
            least_squares( build_pts, 
                           build_vals.reshape( build_vals.shape[0], 1 ), 
                           pce_lsq_oracle );
            pred_vals = pce_lsq_oracle.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            lsq_oracle_errors[k] = l2_error
            print 'lsq', l2_error
            """
            # omp pce
            from indexing_cpp import  get_hyperbolic_indices
            total_degree_indices = PolyIndexVector()
            get_hyperbolic_indices( num_dims, 8, 1, 
                                    total_degree_indices )
            #pce_omp.set_basis_indices( oracle_basis )
            pce_omp.set_basis_indices( total_degree_indices )
            OMP_fast_cv( build_pts, 
                 build_vals.reshape( build_vals.shape[0], 1 ), 
                 pce_omp );
            pred_vals = pce_omp.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            omp_errors[k] = l2_error
            print 'omp', l2_error
            
            # omp oracle pce
            #pce_omp_oracle.set_basis_indices( N_term_basis_indices )
            #pce_omp_oracle.set_basis_indices( oracle_basis )
            pce_omp_oracle.set_basis_indices( total_degree_indices )
            OMP_brute_cv( build_pts, 
                          build_vals.reshape( build_vals.shape[0], 1 ), 
                          pce_omp_oracle );
            pred_vals = pce_omp_oracle.evaluate_set( test_pts ).squeeze()
            l2_error = get_l2_error( test_vals, pred_vals )
            omp_oracle_errors[k] = l2_error
            print 'omp oracle', l2_error
            
            

        least_error[n] = least_errors.mean()
        least_oracle_error[n] = least_oracle_errors.mean()
        #bregman_error[n] = bregman_errors.mean()
        omp_error[n] = omp_errors.mean()
        omp_oracle_error[n] = omp_oracle_errors.mean()
        nesta_error[n] = nesta_errors.mean()
        nesta_oracle_error[n] = nesta_oracle_errors.mean()
        spgl1_error[n] = spgl1_errors.mean()
        lsq_oracle_error[n] = lsq_oracle_errors.mean()
        print 'least interpolant error: ',  least_error[n]
        print 'least interpolant oracle error: ',  least_oracle_error[n]
        print 'omp error: ',  omp_error[n]
        print 'omp oracle error: ',  omp_oracle_error[n]
        print 'nesta error: ',  nesta_error[n]
        print 'nesta oracle error: ',  nesta_oracle_error[n]
        print 'spgl1 error: ',  spgl1_error[n]
        print 'lsq oracle error: ',  lsq_oracle_error[n]
        
    print 'sparse grid ', sg_error
    print 'n term', nterm_pce_error
    print 'least', least_error
    print 'least_oracle', least_oracle_error
    print 'omp error: ',  omp_error
    print 'omp oracle error: ',  omp_oracle_error
    print 'nesta', nesta_error
    print 'nesta oracle', nesta_oracle_error
    print 'spgl1', spgl1_error
    print 'lsq oracle', lsq_oracle_error

    print 'serializing'
    import pickle
    pickle.dump( nterm_pce_error, open( 'nterm_pce_error.p', 'wb' ) )
    pickle.dump( sg_error, open( 'sg_error.p', 'wb' ) )
    pickle.dump( least_error, open( 'least_error.p', 'wb' ) )
    pickle.dump( omp_error, open( 'omp_error.p', 'wb' ) )
    pickle.dump( omp_oracle_error, open( 'omp_oracle_error.p', 'wb' ) )
    pickle.dump( nesta_error, open( 'nesta_error.p', 'wb' ) )
    pickle.dump( nesta_oracle_error, open( 'nesta_oracle_error.p', 'wb' ) )

    print 'serialization complete'

    # plot convergence
    import pylab
    #pylab.loglog( num_pts_sg, sg_error, 'o-', label = 'sparse grid' )
    pylab.loglog( num_pts, nterm_pce_error, 'o-', label = 'best N term')
    
    pylab.loglog( num_pts, least_error, 'o-', label = 'least interpolant')
    #pylab.loglog( num_pts, least_oracle_error, 'o-', 
    #              label = 'oracle least interpolant')
    pylab.loglog( num_pts, omp_error, 'o-', 
                  label = 'OMP')
    pylab.loglog( num_pts, omp_oracle_error, 'o-', 
                  label = 'oracle OMP')
    print nesta_error.shape, nesta_oracle_error.shape
    pylab.loglog( num_pts, nesta_error, 'o-', 
                  label = 'NESTA')
    pylab.loglog( num_pts, nesta_oracle_error, 'o-', 
                  label = 'NESTA oracle')
    #pylab.loglog( num_pts, spgl1_error, 'o-', 
    #              label = 'SPGL1')                  
    #pylab.loglog( num_pts, lsq_oracle_error, 'o-', 
    #              label = 'Least squars')
    pylab.legend()
    pylab.show()
    assert False

    # plot pce coefficients
    from utilities.visualisation import plot_pce_coefficients
    indices = sorted( pce.basis_indices, 
                      key = lambda x: x.norm_of_index( 1 ) )
    coeff = numpy.empty( ( pce.coeff.shape[0] ), numpy.double )
    for i in xrange( len( indices ) ):
        coeff[i] = pce.coeff[indices[i].get_array_index()]
        if ( abs( coeff[i] ) < numpy.finfo( numpy.double ).eps ):
            coeff[i] = 0.
    plot_pce_coefficients( coeff.reshape(coeff.shape[0],1) )