@@ -590,9 +590,11 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
590590
591591 ! Local variables
592592 type(linalg_state_type) :: err0
593- integer(ilp) :: m, n, i
593+ integer(ilp) :: m, n, j
594594 logical(lk) :: copy_a
595- ${rt}$, allocatable :: a_scaled(:,:), b_scaled(:)
595+ ${rt}$, pointer :: amat(:,:)
596+ ${rt}$, allocatable, target :: amat_alloc(:,:)
597+ ${rt}$, allocatable :: b_scaled(:)
596598 real(${rk}$), allocatable :: sqrt_w(:)
597599 character(*), parameter :: this = 'weighted_lstsq'
598600
@@ -602,6 +604,14 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
602604 ! Allocate result (even on error, to prevent segfault on return)
603605 allocate(x(n))
604606
607+ ! Validate matrix dimensions
608+ if (m < 1 .or. n < 1) then
609+ err0 = linalg_state_type(this, LINALG_VALUE_ERROR, 'Invalid matrix size a(m, n) =', [m, n])
610+ call linalg_error_handling(err0, err)
611+ if (present(rank)) rank = 0
612+ return
613+ end if
614+
605615 ! Validate inputs
606616 if (size(w, kind=ilp) /= m) then
607617 err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
@@ -611,6 +621,14 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
611621 return
612622 end if
613623
624+ if (size(b, kind=ilp) /= m) then
625+ err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
626+ 'Right-hand side size must match rows of A:', size(b, kind=ilp), '/=', m)
627+ call linalg_error_handling(err0, err)
628+ if (present(rank)) rank = 0
629+ return
630+ end if
631+
614632 if (any(w <= 0.0_${rk}$)) then
615633 err0 = linalg_state_type(this, LINALG_VALUE_ERROR, 'Weights must be positive')
616634 call linalg_error_handling(err0, err)
@@ -629,28 +647,29 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
629647 allocate(sqrt_w(m))
630648 sqrt_w = sqrt(w)
631649
632- ! Scale A and b
650+ ! Handle A matrix: either copy or use original
633651 if (copy_a) then
634- ! Allocate and scale into copy (preserves original A)
635- allocate(a_scaled(m, n))
636- do i = 1, m
637- a_scaled(i, :) = sqrt_w(i) * a(i, :)
638- end do
652+ allocate(amat_alloc(m, n))
653+ amat => amat_alloc
639654 else
640- ! Scale A in-place (destroys original A)
641- allocate(a_scaled(m, n))
642- do i = 1, m
643- a_scaled(i, :) = sqrt_w(i) * a(i, :)
644- a(i, :) = a_scaled(i, :)
645- end do
655+ amat => a
646656 end if
647657
658+ ! Scale A column-wise (cache-friendly: column-major order)
659+ do j = 1, n
660+ amat(:, j) = sqrt_w(:) * a(:, j)
661+ end do
662+
648663 ! Scale b
649664 allocate(b_scaled(m))
650665 b_scaled = sqrt_w * b
651666
652667 ! Solve transformed OLS problem
653- x = stdlib_linalg_${ri}$_lstsq_one(a_scaled, b_scaled, cond=cond, overwrite_a=.true., rank=rank, err=err)
668+ x = stdlib_linalg_${ri}$_lstsq_one(amat, b_scaled, cond=cond, overwrite_a=.true., rank=rank, err=err)
669+
670+ ! Cleanup
671+ if (copy_a) deallocate(amat_alloc)
672+ deallocate(b_scaled, sqrt_w)
654673
655674 end function stdlib_linalg_${ri}$_weighted_lstsq
656675 #:endfor
@@ -679,7 +698,7 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
679698
680699 ! Local variables
681700 type(linalg_state_type) :: err0
682- integer(ilp) :: m, n, p, lda, ldb, lwork, info
701+ integer(ilp) :: m, n, p, lda, ldb, lwork, info, i, j
683702 logical(lk) :: copy_a, is_prefactored
684703 ${rt}$, pointer :: amat(:,:)
685704 ${rt}$, allocatable, target :: amat_alloc(:,:)
@@ -690,6 +709,16 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
690709 n = size(a, 2, kind=ilp)
691710 p = m ! For GLS, B is m×m
692711
712+ ! Allocate result early (prevents segfault on error return)
713+ allocate(x(n))
714+
715+ ! Validate matrix dimensions
716+ if (m < 1 .or. n < 1) then
717+ err0 = linalg_state_type(this, LINALG_VALUE_ERROR, 'Invalid matrix size a(m, n) =', [m, n])
718+ call linalg_error_handling(err0, err)
719+ return
720+ end if
721+
693722 ! Validate sizes
694723 if (size(w, 1, kind=ilp) /= m .or. size(w, 2, kind=ilp) /= m) then
695724 err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
@@ -698,6 +727,20 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
698727 return
699728 end if
700729
730+ if (size(b, kind=ilp) /= m) then
731+ err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
732+ 'Right-hand side size must match rows of A:', size(b, kind=ilp), '/=', m)
733+ call linalg_error_handling(err0, err)
734+ return
735+ end if
736+
737+ if (m < n) then
738+ err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
739+ 'GGGLM requires m >= n (overdetermined or square):', m, '<', n)
740+ call linalg_error_handling(err0, err)
741+ return
742+ end if
743+
701744 ! Process options
702745 is_prefactored = .false._lk
703746 if (present(prefactored_w)) is_prefactored = prefactored_w
@@ -708,9 +751,6 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
708751 copy_a = .true._lk
709752 end if
710753
711- ! Allocate result
712- allocate(x(n))
713-
714754 ! Handle A matrix
715755 if (copy_a) then
716756 allocate(amat_alloc(m, n), source=a)
@@ -734,6 +774,9 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
734774 err0 = linalg_state_type(this, LINALG_VALUE_ERROR, &
735775 'Invalid argument to POTRF at position', -info)
736776 end if
777+ ! Cleanup before early return
778+ if (copy_a) deallocate(amat_alloc)
779+ deallocate(lmat)
737780 call linalg_error_handling(err0, err)
738781 return
739782 end if
@@ -746,10 +789,18 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
746789 lda = m
747790 ldb = m
748791
792+ ! Zero out upper triangle of lmat (GGGLM reads full matrix,
793+ ! but potrf only sets lower triangle)
794+ do j = 1, m
795+ do i = 1, j - 1
796+ lmat(i, j) = 0.0_${rk}$
797+ end do
798+ end do
799+
749800 ! Workspace query
750801 allocate(work(1))
751802 call ggglm(m, n, p, amat, lda, lmat, ldb, d, x, y, work, -1_ilp, info)
752- lwork = int (real(work(1), ${rk}$), ilp)
803+ lwork = ceiling (real(work(1), kind= ${rk}$), kind= ilp)
753804 deallocate(work)
754805 allocate(work(lwork))
755806
@@ -758,6 +809,11 @@ submodule (stdlib_linalg) stdlib_linalg_least_squares
758809
759810 ! Handle errors
760811 call handle_ggglm_info(this, info, m, n, p, err0)
812+
813+ ! Cleanup
814+ if (copy_a) deallocate(amat_alloc)
815+ deallocate(lmat, d, y, work)
816+
761817 call linalg_error_handling(err0, err)
762818
763819 end function stdlib_linalg_${ri}$_generalized_lstsq
0 commit comments