nonlin 1.5.2
A library that provides routines to compute the solutions to systems of nonlinear equations.
Loading...
Searching...
No Matches
nonlin_polynomials.f90
1! nonlin_polynomials.f90
2
3
4! TO DO:
5! - Multiplication (convolution)
6! - Division (deconvolution)
7! - C interface
8
14 use, intrinsic :: iso_fortran_env, only : int32, real64
15 use linalg, only : eigen, solve_least_squares
16 use ferror, only : errors
17 use nonlin_constants, only : nl_invalid_input_error, nl_array_size_error, &
18 nl_out_of_memory_error
19 implicit none
20
21private
22public :: polynomial
23public :: assignment(=)
24public :: operator(+)
25public :: operator(-)
26public :: operator(*)
27
28! ******************************************************************************
29! INTERFACES
30! ------------------------------------------------------------------------------
32interface assignment(=)
33 module procedure :: poly_equals
34 module procedure :: poly_dbl_equals
35 module procedure :: poly_equals_array
36end interface
37
39interface operator(+)
40 module procedure :: poly_poly_add
41end interface
42
44interface operator(-)
45 module procedure :: poly_poly_subtract
46end interface
47
49interface operator(*)
50 module procedure :: poly_poly_mult
51 module procedure :: poly_dbl_mult
52 module procedure :: dbl_poly_mult
53end interface
54
55! ******************************************************************************
56! TYPES
57! ------------------------------------------------------------------------------
61private
63 real(real64), allocatable, dimension(:) :: m_coeffs
64contains
66 generic, public :: initialize => init_poly, init_poly_coeffs
68 procedure, public :: order => get_poly_order
70 procedure, public :: fit => poly_fit
73 procedure, public :: fit_thru_zero => poly_fit_thru_zero
75 generic, public :: evaluate => evaluate_real, evaluate_complex
77 procedure, public :: companion_mtx => poly_companion_mtx
79 procedure, public :: roots => poly_roots
81 procedure, public :: get => get_poly_coefficient
83 procedure, public :: get_all => get_poly_coefficients
85 procedure, public :: set => set_poly_coefficient
86
87 procedure :: evaluate_real => poly_eval_double
88 procedure :: evaluate_complex => poly_eval_complex
89 procedure :: init_poly
90 procedure :: init_poly_coeffs
91end type
92
93contains
94! ******************************************************************************
95! POLYNOMIAL MEMBERS
96! ------------------------------------------------------------------------------
110 subroutine init_poly(this, order, err)
111 ! Arguments
112 class(polynomial), intent(inout) :: this
113 integer(int32), intent(in) :: order
114 class(errors), intent(inout), optional, target :: err
115
116 ! Parameters
117 real(real64), parameter :: zero = 0.0d0
118
119 ! Local Variables
120 integer(int32) :: n, istat
121 class(errors), pointer :: errmgr
122 type(errors), target :: deferr
123
124 ! Initialization
125 n = order + 1
126 if (present(err)) then
127 errmgr => err
128 else
129 errmgr => deferr
130 end if
131
132 ! Input Check
133 if (order < 0) then
134 ! ERROR: Negative order is not supported
135 call errmgr%report_error("init_polynomial", &
136 "A negative polynomial order is not supported.", &
137 nl_invalid_input_error)
138 return
139 end if
140
141 ! Process
142 if (allocated(this%m_coeffs)) deallocate(this%m_coeffs)
143 allocate(this%m_coeffs(n), stat = istat)
144 if (istat /= 0) then
145 ! ERROR: Out of memory
146 call errmgr%report_error("init_polynomial", &
147 "Insufficient memory available.", nl_out_of_memory_error)
148 return
149 end if
150 this%m_coeffs = zero
151 end subroutine
152
153! ------------------------------------------------------------------------------
167 subroutine init_poly_coeffs(this, c, err)
168 ! Arguments
169 class(polynomial), intent(inout) :: this
170 real(real64), intent(in), dimension(:) :: c
171 class(errors), intent(inout), optional, target :: err
172
173 ! Local Variables
174 integer(int32) :: i, n
175 class(errors), pointer :: errmgr
176 type(errors), target :: deferr
177
178 ! Initialization
179 n = size(c)
180 if (present(err)) then
181 errmgr => err
182 else
183 errmgr => deferr
184 end if
185
186 ! Initialize the polynomial
187 call init_poly(this, n - 1, errmgr)
188 if (errmgr%has_error_occurred()) return
189
190 ! Populate the polynomial coefficients
191 do i = 1, n
192 call this%set(i, c(i))
193 end do
194 end subroutine
195
196! ------------------------------------------------------------------------------
203 pure function get_poly_order(this) result(n)
204 class(polynomial), intent(in) :: this
205 integer(int32) :: n
206 if (.not.allocated(this%m_coeffs)) then
207 n = -1
208 else
209 n = size(this%m_coeffs) - 1
210 end if
211 end function
212
213! ------------------------------------------------------------------------------
284 subroutine poly_fit(this, x, y, order, err)
285 ! Arguments
286 class(polynomial), intent(inout) :: this
287 real(real64), intent(in), dimension(:) :: x
288 real(real64), intent(inout), dimension(:) :: y
289 integer(int32), intent(in) :: order
290 class(errors), intent(inout), optional, target :: err
291
292 ! Parameters
293 real(real64), parameter :: one = 1.0d0
294
295 ! Local Variables
296 integer(int32):: j, n, ncols, flag
297 real(real64), pointer, dimension(:,:) :: a
298 class(errors), pointer :: errmgr
299 type(errors), target :: deferr
300
301 ! Initialization
302 n = size(x)
303 ncols = order + 1
304 if (present(err)) then
305 errmgr => err
306 else
307 errmgr => deferr
308 end if
309
310 ! Input Check
311 if (size(y) /= n) then
312 ! ERROR: Array size mismatch
313 call errmgr%report_error("polynomial_fit", "Array size mismatch.", &
314 nl_array_size_error)
315 return
316 else if (order >= n .or. order < 1) then
317 ! ERROR: Requested order does not make sense
318 call errmgr%report_error("polynomial_fit", "The requested " // &
319 "polynomial order is not valid for this data set.", &
320 nl_invalid_input_error)
321 return
322 end if
323
324 ! Local Memory Allocation
325 allocate(a(n,ncols), stat = flag)
326 if (flag /= 0) then
327 ! ERROR: Out of memory
328 call errmgr%report_error("polynomial_fit", &
329 "Insufficient memory available.", nl_out_of_memory_error)
330 return
331 end if
332
333 ! Ensure the polynomial object is initialized and sized appropriately
334 if (this%order() /= order) then
335 call this%initialize(order, errmgr)
336 if (errmgr%has_error_occurred()) return
337 end if
338
339 ! Populate A
340 do j = 1, n
341 a(j,1) = one
342 a(j,2) = x(j)
343 end do
344 do j = 3, ncols
345 a(:,j) = a(:,j-1) * x
346 end do
347
348 ! Solve: A * coeffs = y
349 call solve_least_squares(a, y, err = errmgr)
350 if (errmgr%has_error_occurred()) return
351
352 ! Extract the coefficients from the first order+1 elements of Y
353 this%m_coeffs = y(1:ncols)
354 end subroutine
355
356! ------------------------------------------------------------------------------
375 subroutine poly_fit_thru_zero(this, x, y, order, err)
376 ! Arguments
377 class(polynomial), intent(inout) :: this
378 real(real64), intent(in), dimension(:) :: x
379 real(real64), intent(inout), dimension(:) :: y
380 integer(int32), intent(in) :: order
381 class(errors), intent(inout), optional, target :: err
382
383 ! Parameters
384 real(real64), parameter :: zero = 0.0d0
385
386 ! Local Variables
387 integer(int32):: j, n, ncols, flag
388 real(real64), pointer, dimension(:,:) :: a
389 class(errors), pointer :: errmgr
390 type(errors), target :: deferr
391
392 ! Initialization
393 n = size(x)
394 ncols = order
395 if (present(err)) then
396 errmgr => err
397 else
398 errmgr => deferr
399 end if
400
401 ! Input Check
402 if (size(y) /= n) then
403 ! ERROR: Array size mismatch
404 call errmgr%report_error("polynomial_fit_thru_zero", &
405 "Array size mismatch.", nl_array_size_error)
406 return
407 else if (order >= n .or. order < 1) then
408 ! ERROR: Requested order does not make sense
409 call errmgr%report_error("polynomial_fit_thru_zero", &
410 "The requested polynomial order is not valid for this " // &
411 "data set.", nl_invalid_input_error)
412 return
413 end if
414
415 ! Local Memory Allocation
416 allocate(a(n,ncols), stat = flag)
417 if (flag /= 0) then
418 ! ERROR: Out of memory
419 call errmgr%report_error("polynomial_fit_thru_zero", &
420 "Insufficient memory available.", nl_out_of_memory_error)
421 return
422 end if
423
424 ! Ensure the polynomial object is initialized and sized appropriately
425 if (this%order() /= order) then
426 call this%initialize(order, errmgr)
427 if (errmgr%has_error_occurred()) return
428 end if
429
430 ! Populate A
431 a(:,1) = x
432 do j = 2, ncols
433 a(:,j) = a(:,j-1) * x
434 end do
435
436 ! Solve: A * coeffs = y
437 call solve_least_squares(a, y, err = errmgr)
438 if (errmgr%has_error_occurred()) return
439
440 ! Extract the coefficients from the first order+1 elements of Y
441 this%m_coeffs(1) = zero
442 this%m_coeffs(2:ncols+1) = y(1:ncols)
443 end subroutine
444
445! ------------------------------------------------------------------------------
452 elemental function poly_eval_double(this, x) result(y)
453 ! Arguments
454 class(polynomial), intent(in) :: this
455 real(real64), intent(in) :: x
456 real(real64) :: y
457
458 ! Parameters
459 real(real64), parameter :: zero = 0.0d0
460
461 ! Local Variables
462 integer(int32) :: j, order, n
463
464 ! Initialization
465 order = this%order()
466 n = order + 1
467 if (order == -1) then
468 y = zero
469 return
470 else if (order == 0) then
471 y = this%m_coeffs(1)
472 return
473 end if
474
475 ! Process
476 y =this%m_coeffs(n) * x + this%m_coeffs(order)
477 do j = n - 2, 1, -1
478 y = y * x + this%m_coeffs(j)
479 end do
480 end function
481
482! ------------------------------------------------------------------------------
489 elemental function poly_eval_complex(this, x) result(y)
490 ! Arguments
491 class(polynomial), intent(in) :: this
492 complex(real64), intent(in) :: x
493 complex(real64) :: y
494
495 ! Parameters
496 complex(real64), parameter :: zero = (0.0d0, 0.0d0)
497
498 ! Local Variables
499 integer(int32) :: j, order, n
500
501 ! Initialization
502 order = this%order()
503 n = order + 1
504 if (order == -1) then
505 y = zero
506 return
507 else if (order == 0) then
508 y = this%m_coeffs(1)
509 return
510 end if
511
512 ! Process
513 y =this%m_coeffs(n) * x + this%m_coeffs(order)
514 do j = n - 2, 1, -1
515 y = y * x + this%m_coeffs(j)
516 end do
517 end function
518
519! ------------------------------------------------------------------------------
529 pure function poly_companion_mtx(this) result(c)
530 ! Arguments
531 class(polynomial), intent(in) :: this
532 real(real64), dimension(this%order(), this%order()) :: c
533
534 ! Parameters
535 real(real64), parameter :: zero = 0.0d0
536 real(real64), parameter :: one = 1.0d0
537
538 ! Local Variables
539 integer(int32) :: i, n
540
541 ! Process
542 n = this%order()
543 if (n == -1) return
544 c = zero
545 do i = 1, n
546 c(i,n) = -this%m_coeffs(i) / this%m_coeffs(n + 1)
547 if (i < n) c(i+1,i) = one
548 end do
549 end function
550
551! ------------------------------------------------------------------------------
614 function poly_roots(this, err) result(z)
615 ! Arguments
616 class(polynomial), intent(in) :: this
617 complex(real64), dimension(this%order()) :: z
618 class(errors), intent(inout), optional, target :: err
619
620 ! Local Variables
621 integer(int32) :: n
622 real(real64), allocatable, dimension(:,:) :: c
623
624 ! Initialization
625 n = this%order()
626
627 ! Quick Return
628 if (n == 0) return
629
630 ! Compute the companion matrix
631 c = this%companion_mtx()
632
633 ! Compute the eigenvalues of the companion matrix. The eigenvalues are
634 ! the roots of the polynomial.
635 call eigen(c, z, err = err)
636 end function
637
638! ------------------------------------------------------------------------------
655 function get_poly_coefficient(this, ind, err) result(c)
656 ! Arguments
657 class(polynomial), intent(in) :: this
658 integer(int32), intent(in) :: ind
659 class(errors), intent(inout), optional, target :: err
660 real(real64) :: c
661
662 ! Local Variables
663 class(errors), pointer :: errmgr
664 type(errors), target :: deferr
665
666 ! Initialization
667 c = 0.0d0
668 if (present(err)) then
669 errmgr => err
670 else
671 errmgr => deferr
672 end if
673
674 ! Quick Return
675 if (this%order() == -1) return
676
677 ! Input Check
678 if (ind <= 0 .or. ind > this%order() + 1) then
679 ! ERROR: Index out of range
680 call errmgr%report_error("get_polynomial_coefficient", &
681 "The specified index is outside the bounds of the " // &
682 "coefficient array.", nl_invalid_input_error)
683 return
684 end if
685
686 ! Get the coefficient
687 c = this%m_coeffs(ind)
688 end function
689
690! ------------------------------------------------------------------------------
698 pure function get_poly_coefficients(this) result(c)
699 ! Arguments
700 class(polynomial), intent(in) :: this
701 real(real64), dimension(this%order() + 1) :: c
702
703 ! Process
704 if (this%order() == -1) return
705 c = this%m_coeffs
706 end function
707
708! ------------------------------------------------------------------------------
724 subroutine set_poly_coefficient(this, ind, c, err)
725 ! Arguments
726 class(polynomial), intent(inout) :: this
727 integer(int32), intent(in) :: ind
728 real(real64), intent(in) :: c
729 class(errors), intent(inout), optional, target :: err
730
731 ! Local Variables
732 class(errors), pointer :: errmgr
733 type(errors), target :: deferr
734
735 ! Initialization
736 if (present(err)) then
737 errmgr => err
738 else
739 errmgr => deferr
740 end if
741
742 ! Quick Return
743 if (this%order() == -1) return
744
745 ! Input Check
746 if (ind <= 0 .or. ind > this%order() + 1) then
747 ! ERROR: Index out of range
748 call errmgr%report_error("set_polynomial_coefficient", &
749 "The specified index is outside the bounds of the " // &
750 "coefficient array.", nl_invalid_input_error)
751 return
752 end if
753
754 ! Process
755 this%m_coeffs(ind) = c
756 end subroutine
757
758! ******************************************************************************
759! OPERATORS
760! ------------------------------------------------------------------------------
765 subroutine poly_equals(x, y)
766 ! Arguments
767 class(polynomial), intent(inout) :: x
768 class(polynomial), intent(in) :: y
769
770 ! Local Variables
771 integer(int32) :: i, ord
772
773 ! Process
774 ord = y%order()
775 if (x%order() /= ord) call x%initialize(ord)
776 do i = 1, ord + 1
777 call x%set(i, y%get(i))
778 end do
779 end subroutine
780
781! ------------------------------------------------------------------------------
786 subroutine poly_dbl_equals(x, y)
787 ! Arguments
788 class(polynomial), intent(inout) :: x
789 real(real64), intent(in) :: y
790
791 ! Local Variables
792 integer(int32) :: i, ord
793
794 ! Process
795 ord = x%order()
796 do i = 1, ord + 1
797 call x%set(i, y)
798 end do
799 end subroutine
800
801! ------------------------------------------------------------------------------
806 subroutine poly_equals_array(x, y)
807 ! Arguments
808 class(polynomial), intent(inout) :: x
809 real(real64), intent(in), dimension(:) :: y
810 call x%initialize(y)
811 end subroutine
812
813! ------------------------------------------------------------------------------
820 function poly_poly_add(x, y) result(z)
821 ! Arguments
822 class(polynomial), intent(in) :: x, y
823 type(polynomial) :: z
824
825 ! Local Variables
826 integer(int32) :: i, max_ord, x_ord, y_ord
827
828 ! Initialization
829 x_ord = x%order()
830 y_ord = y%order()
831 max_ord = max(x_ord, y_ord)
832 call z%initialize(max_ord)
833
834 ! Quick Return
835 if (x_ord == -1 .and. y_ord == -1) return
836 if (x_ord == -1 .and. y_ord /= -1) then
837 do i = 1, max_ord + 1
838 call z%set(i, y%get(i))
839 end do
840 return
841 else if (x_ord /= -1 .and. y_ord == -1) then
842 do i = 1, max_ord + 1
843 call z%set(i, x%get(i))
844 end do
845 return
846 end if
847
848 ! Process
849 if (x_ord > y_ord) then
850 do i = 1, y_ord + 1
851 call z%set(i, x%get(i) + y%get(i))
852 end do
853 do i = y_ord + 2, x_ord
854 call z%set(i, x%get(i))
855 end do
856 else if (x_ord < y_ord) then
857 do i = 1, x_ord + 1
858 call z%set(i, x%get(i) + y%get(i))
859 end do
860 do i = x_ord + 2, y_ord + 1
861 call z%set(i, y%get(i))
862 end do
863 else
864 do i = 1, max_ord + 1
865 call z%set(i, x%get(i) + y%get(i))
866 end do
867 end if
868 end function
869
870! ------------------------------------------------------------------------------
877 function poly_poly_subtract(x, y) result(z)
878 ! Arguments
879 class(polynomial), intent(in) :: x, y
880 type(polynomial) :: z
881
882 ! Local Variables
883 integer(int32) :: i, max_ord, x_ord, y_ord
884
885 ! Initialization
886 x_ord = x%order()
887 y_ord = y%order()
888 max_ord = max(x_ord, y_ord)
889 call z%initialize(max_ord)
890
891 ! Quick Return
892 if (x_ord == -1 .and. y_ord == -1) return
893 if (x_ord == -1 .and. y_ord /= -1) then
894 do i = 1, max_ord + 1
895 call z%set(i, y%get(i))
896 end do
897 return
898 else if (x_ord /= -1 .and. y_ord == -1) then
899 do i = 1, max_ord + 1
900 call z%set(i, x%get(i))
901 end do
902 return
903 end if
904
905 ! Process
906 if (x_ord > y_ord) then
907 do i = 1, y_ord + 1
908 call z%set(i, x%get(i) - y%get(i))
909 end do
910 do i = y_ord + 2, x_ord
911 call z%set(i, x%get(i))
912 end do
913 else if (x_ord < y_ord) then
914 do i = 1, x_ord + 1
915 call z%set(i, x%get(i) - y%get(i))
916 end do
917 do i = x_ord + 2, y_ord + 1
918 call z%set(i, -y%get(i))
919 end do
920 else
921 do i = 1, max_ord + 1
922 call z%set(i, x%get(i) - y%get(i))
923 end do
924 end if
925 end function
926
927! ------------------------------------------------------------------------------
934 function poly_poly_mult(x, y) result(z)
935 ! Arguments
936 class(polynomial), intent(in) :: x, y
937 type(polynomial) :: z
938
939 ! Local Variables
940 integer(int32) :: i, j, m, n
941 real(real64) :: val
942
943 ! Initialization
944 n = x%order() + 1
945 m = y%order() + 1
946 call z%initialize(x%order() + y%order()) ! Sets z to all zeros
947
948 ! Process
949 do i = 1, n
950 do j = 1, m
951 val = z%get(i + j - 1) + x%get(i) * y%get(j)
952 call z%set(i + j - 1, val)
953 end do
954 end do
955 end function
956
957! ------------------------------------------------------------------------------
964 function poly_dbl_mult(x, y) result(z)
965 ! Arguments
966 class(polynomial), intent(in) :: x
967 real(real64), intent(in) :: y
968 type(polynomial) :: z
969
970 ! Local Variables
971 integer(int32) :: i, ord
972
973 ! Process
974 ord = x%order()
975 call z%initialize(ord)
976 do i = 1, ord + 1
977 call z%set(i, x%get(i) * y)
978 end do
979 end function
980
981! ------------------------------------------------------------------------------
988 function dbl_poly_mult(x, y) result(z)
989 ! Arguments
990 real(real64), intent(in) :: x
991 class(polynomial), intent(in) :: y
992 type(polynomial) :: z
993
994 ! Local Variables
995 integer(int32) :: i, ord
996
997 ! Process
998 ord = y%order()
999 call z%initialize(ord)
1000 do i = 1, ord + 1
1001 call z%set(i, y%get(i) * x)
1002 end do
1003 end function
1004
1005! ------------------------------------------------------------------------------
1006
1007! Example Polynomial Code (Coefficients go from lowest order to highest)
1008! src: https://github.com/JuliaMath/Polynomials.jl
1009!
1010! function *{T,S}(p1::Poly{T}, p2::Poly{S})
1011! if p1.var != p2.var
1012! error("Polynomials must have same variable")
1013! end
1014! R = promote_type(T,S)
1015! n = length(p1)-1
1016! m = length(p2)-1
1017! a = zeros(R,m+n+1)
1018
1019! for i = 0:n
1020! for j = 0:m
1021! a[i+j+1] += p1[i] * p2[j]
1022! end
1023! end
1024! Poly(a,p1.var)
1025! end
1026
1027! ## older . operators, hack to avoid warning on v0.6
1028! dot_operators = quote
1029! @compat Base.:.+{T<:Number}(c::T, p::Poly) = +(p, c)
1030! @compat Base.:.+{T<:Number}(p::Poly, c::T) = +(p, c)
1031! @compat Base.:.-{T<:Number}(p::Poly, c::T) = +(p, -c)
1032! @compat Base.:.-{T<:Number}(c::T, p::Poly) = +(p, -c)
1033! @compat Base.:.*{T<:Number,S}(c::T, p::Poly{S}) = Poly(c * p.a, p.var)
1034! @compat Base.:.*{T<:Number,S}(p::Poly{S}, c::T) = Poly(p.a * c, p.var)
1035! end
1036! VERSION < v"0.6.0-dev" && eval(dot_operators)
1037
1038
1039! # are any values NaN
1040! hasnan(p::Poly) = reduce(|, (@compat isnan.(p.a)))
1041
1042! function divrem{T, S}(num::Poly{T}, den::Poly{S})
1043! if num.var != den.var
1044! error("Polynomials must have same variable")
1045! end
1046! m = length(den)-1
1047! if m == 0 && den[0] == 0
1048! throw(DivideError())
1049! end
1050! R = typeof(one(T)/one(S))
1051! n = length(num)-1
1052! deg = n-m+1
1053! if deg <= 0
1054! return convert(Poly{R}, zero(num)), convert(Poly{R}, num)
1055! end
1056
1057! aQ = zeros(R, deg)
1058! # aR = deepcopy(num.a)
1059! # @show num.a
1060! aR = R[ num.a[i] for i = 1:n+1 ]
1061! for i = n:-1:m
1062! quot = aR[i+1] / den[m]
1063! aQ[i-m+1] = quot
1064! for j = 0:m
1065! elem = den[j]*quot
1066! aR[i-(m-j)+1] -= elem
1067! end
1068! end
1069! pQ = Poly(aQ, num.var)
1070! pR = Poly(aR, num.var)
1071
1072! return pQ, pR
1073! end
1074
1075! div(num::Poly, den::Poly) = divrem(num, den)[1]
1076! rem(num::Poly, den::Poly) = divrem(num, den)[2]
1077
1078! ==(p1::Poly, p2::Poly) = (p1.var == p2.var && p1.a == p2.a)
1079! ==(p1::Poly, n::Number) = (coeffs(p1) == [n])
1080! ==(n::Number, p1::Poly) = (p1 == n)
1081end module
nonlin_constants
Defines a polynomial, and associated routines for performing polynomial operations.