-- | Gaussian quadrature over [-1, 1] using the Legendre polynomials.
--
module Integration.Gaussian (
+ gaussian,
+ gaussian',
jacobi_matrix,
nodes_and_weights )
where
import Algebra.Absolute ( abs )
+import qualified Algebra.Absolute as Absolute ( C )
import qualified Algebra.Algebraic as Algebraic ( C )
+import qualified Algebra.Field as Field ( C )
import qualified Algebra.Ring as Ring ( C )
+import qualified Algebra.ToRational as ToRational ( C )
import Data.Vector.Fixed ( Arity, S )
import NumericPrelude hiding ( abs )
import Linear.Matrix (
Col,
+ Col10,
Mat(..),
+ colzipwith,
construct,
+ fromList,
identity_matrix,
matmap,
row',
transpose )
import Linear.QR ( eigenvectors_symmetric )
+import Normed ( Normed(..) )
-- | Compute the Jacobi matrix for the Legendre polynomials over
--
-- Examples:
--
--- >>> import Linear.Matrix ( Mat2, frobenius_norm, fromList )
+-- >>> import Linear.Matrix ( Mat2, frobenius_norm )
--
-- >>> let c = 1/(sqrt 3)
-- >>> let expected = fromList [[0,c],[c, 0]] :: Mat2 Double
--
-- Examples:
--
--- >>> import Linear.Matrix ( Col2, frobenius_norm, fromList )
+-- >>> import Linear.Matrix ( Col2, frobenius_norm )
--
-- >>> let (ns,ws) = nodes_and_weights 1000 :: (Col2 Double, Col2 Double)
-- >>> let c = 1/(sqrt 3)
weights_row = matmap (\x -> (fromInteger 2)*x^2) first_components
weights = transpose $ weights_row
+
+
+-- | Integrate a function over [-1,1] using Gaussian quadrature. This
+-- is the \"simple\" version of the 'gaussian'' function; here the ten
+-- nodes and weights are fixed.
+--
+-- Examples:
+--
+-- >>> let f x = x * (sin x)
+-- >>> let actual = gaussian f :: Double
+-- >>> let expected = 0.6023373578795136
+-- >>> abs (actual - expected) < 1e-12
+-- True
+--
+gaussian :: forall a.
+ (Absolute.C a, Algebraic.C a, ToRational.C a, Field.C a)
+ => (a -> a) -- ^ The function to integrate.
+ -> a
+gaussian f =
+ gaussian' f nodes weights
+ where
+ coerce = fromRational' . toRational
+
+ nodes :: Col10 a
+ nodes = fromList $ map (map coerce) node_list
+
+ node_list :: [[Double]]
+ node_list = [[-0.9739065285171726],
+ [-0.8650633666889886],
+ [-0.6794095682990235],
+ [-0.43339539412924943],
+ [-0.14887433898163027],
+ [0.14887433898163094],
+ [0.4333953941292481],
+ [0.6794095682990247],
+ [0.8650633666889846],
+ [0.9739065285171717]]
+
+ weights :: Col10 a
+ weights = fromList $ map (map coerce) weight_list
+
+ weight_list :: [[Double]]
+ weight_list = [[0.06667134430868775],
+ [0.14945134915057925],
+ [0.21908636251598282],
+ [0.26926671930999607],
+ [0.295524224714752],
+ [0.295524224714754],
+ [0.26926671930999635],
+ [0.219086362515982],
+ [0.14945134915058048],
+ [0.06667134430868814]]
+
+
+
+-- | Integrate a function over [-1,1] using Gaussian quadrature. The
+-- nodes and the weights must be given (due to the design of the
+-- library, the number of nodes/weights must be requested at the
+-- type level so this function couldn't just compute e.g. @n@ of
+-- them for you).
+--
+-- The class constraints on @a@ could be loosened significantly if
+-- we implemented column sum outside of the 1-norm.
+--
+-- Examples:
+--
+-- >>> import Linear.Matrix ( Col5 )
+--
+-- >>> let (ns, ws) = nodes_and_weights 100 :: (Col5 Double, Col5 Double)
+-- >>> let f x = x * (sin x)
+-- >>> let actual = gaussian' f ns ws
+-- >>> let expected = 0.6023373578795136
+-- >>> abs (actual - expected) < 1e-8
+-- True
+--
+gaussian' :: forall m a.
+ (Arity m, Absolute.C a, Algebraic.C a, ToRational.C a, Ring.C a)
+ => (a -> a) -- ^ The function @f@ to integrate.
+ -> Col (S m) a -- ^ Column matrix of nodes
+ -> Col (S m) a -- ^ Column matrix of weights
+ -> a
+gaussian' f nodes weights =
+ -- The one norm is just the sum of the entries, which is what we
+ -- want.
+ norm_p (1::Int) weighted_values
+ where
+ function_values = matmap f nodes
+ weighted_values = colzipwith (*) weights function_values