team-10/env/Lib/site-packages/scipy/_lib/pyprima/cobyla/initialize.py
2025-08-02 07:34:44 +02:00

215 lines
9.2 KiB
Python

'''
This module contains subroutines for initialization.
Translated from Zaikun Zhang's modern-Fortran reference implementation in PRIMA.
Dedicated to late Professor M. J. D. Powell FRS (1936--2015).
Python translation by Nickolai Belakovski.
'''
from ..common.checkbreak import checkbreak_con
from ..common.consts import DEBUGGING, REALMAX
from ..common.infos import INFO_DEFAULT
from ..common.evaluate import evaluate
from ..common.history import savehist
from ..common.linalg import inv
from ..common.message import fmsg
from ..common.selectx import savefilt
import numpy as np
def initxfc(calcfc, iprint, maxfun, constr0, amat, bvec, ctol, f0, ftarget, rhobeg, x0,
xhist, fhist, chist, conhist, maxhist):
'''
This subroutine does the initialization concerning X, function values, and
constraints.
'''
# Local variables
solver = 'COBYLA'
srname = "INITIALIZE"
# Sizes
num_constraints = np.size(constr0)
m_lcon = np.size(bvec) if bvec is not None else 0
m_nlcon = num_constraints - m_lcon
num_vars = np.size(x0)
# Preconditions
if DEBUGGING:
assert num_constraints >= 0, f'M >= 0 {srname}'
assert num_vars >= 1, f'N >= 1 {srname}'
assert abs(iprint) <= 3, f'IPRINT is 0, 1, -1, 2, -2, 3, or -3 {srname}'
# assert conmat.shape == (num_constraints , num_vars + 1), f'CONMAT.shape = [M, N+1] {srname}'
# assert cval.size == num_vars + 1, f'CVAL.size == N+1 {srname}'
# assert maxchist * (maxchist - maxhist) == 0, f'CHIST.shape == 0 or MAXHIST {srname}'
# assert conhist.shape[0] == num_constraints and maxconhist * (maxconhist - maxhist) == 0, 'CONHIST.shape[0] == num_constraints, SIZE(CONHIST, 2) == 0 or MAXHIST {srname)}'
# assert maxfhist * (maxfhist - maxhist) == 0, f'FHIST.shape == 0 or MAXHIST {srname}'
# assert xhist.shape[0] == num_vars and maxxhist * (maxxhist - maxhist) == 0, 'XHIST.shape[0] == N, SIZE(XHIST, 2) == 0 or MAXHIST {srname)}'
assert all(np.isfinite(x0)), f'X0 is finite {srname}'
assert rhobeg > 0, f'RHOBEG > 0 {srname}'
#====================#
# Calculation starts #
#====================#
# Initialize info to the default value. At return, a value different from this
# value will indicate an abnormal return
info = INFO_DEFAULT
# Initialize the simplex. It will be revised during the initialization.
sim = np.eye(num_vars, num_vars+1) * rhobeg
sim[:, num_vars] = x0
# Initialize the matrix simi. In most cases simi is overwritten, but not always.
simi = np.eye(num_vars) / rhobeg
# evaluated[j] = True iff the function/constraint of SIM[:, j] has been evaluated.
evaluated = np.zeros(num_vars+1, dtype=bool)
# Initialize fval
fval = np.zeros(num_vars+1) + REALMAX
cval = np.zeros(num_vars+1) + REALMAX
conmat = np.zeros((num_constraints, num_vars+1)) + REALMAX
for k in range(num_vars + 1):
x = sim[:, num_vars].copy()
# We will evaluate F corresponding to SIM(:, J).
if k == 0:
j = num_vars
f = f0
constr = constr0
else:
j = k - 1
x[j] += rhobeg
f, constr = evaluate(calcfc, x, m_nlcon, amat, bvec)
cstrv = np.max(np.append(0, constr))
# Print a message about the function/constraint evaluation according to IPRINT.
fmsg(solver, 'Initialization', iprint, k, rhobeg, f, x, cstrv, constr)
# Save X, F, CONSTR, CSTRV into the history.
savehist(maxhist, x, xhist, f, fhist, cstrv, chist, constr, conhist)
# Save F, CONSTR, and CSTRV to FVAL, CONMAT, and CVAL respectively.
evaluated[j] = True
fval[j] = f
conmat[:, j] = constr
cval[j] = cstrv
# Check whether to exit.
subinfo = checkbreak_con(maxfun, k, cstrv, ctol, f, ftarget, x)
if subinfo != INFO_DEFAULT:
info = subinfo
break
# Exchange the new vertex of the initial simplex with the optimal vertex if necessary.
# This is the ONLY part that is essentially non-parallel.
if j < num_vars and fval[j] < fval[num_vars]:
fval[j], fval[num_vars] = fval[num_vars], fval[j]
cval[j], cval[num_vars] = cval[num_vars], cval[j]
conmat[:, [j, num_vars]] = conmat[:, [num_vars, j]]
sim[:, num_vars] = x
sim[j, :j+1] = -rhobeg # SIM[:, :j+1] is lower triangular
nf = np.count_nonzero(evaluated)
if evaluated.all():
# Initialize SIMI to the inverse of SIM[:, :num_vars]
simi = inv(sim[:, :num_vars])
#==================#
# Calculation ends #
#==================#
# Postconditions
if DEBUGGING:
assert nf <= maxfun, f'NF <= MAXFUN {srname}'
assert evaluated.size == num_vars + 1, f'EVALUATED.size == Num_vars + 1 {srname}'
# assert chist.size == maxchist, f'CHIST.size == MAXCHIST {srname}'
# assert conhist.shape== (num_constraints, maxconhist), f'CONHIST.shape == [M, MAXCONHIST] {srname}'
assert conmat.shape == (num_constraints, num_vars + 1), f'CONMAT.shape = [M, N+1] {srname}'
assert not (np.isnan(conmat).any() or np.isneginf(conmat).any()), f'CONMAT does not contain NaN/-Inf {srname}'
assert cval.size == num_vars + 1 and not (any(cval < 0) or any(np.isnan(cval)) or any(np.isposinf(cval))), f'CVAL.shape == Num_vars+1 and CVAL does not contain negative values or NaN/+Inf {srname}'
# assert fhist.shape == maxfhist, f'FHIST.shape == MAXFHIST {srname}'
# assert maxfhist * (maxfhist - maxhist) == 0, f'FHIST.shape == 0 or MAXHIST {srname}'
assert fval.size == num_vars + 1 and not (any(np.isnan(fval)) or any(np.isposinf(fval))), f'FVAL.shape == Num_vars+1 and FVAL is not NaN/+Inf {srname}'
# assert xhist.shape == (num_vars, maxxhist), f'XHIST.shape == [N, MAXXHIST] {srname}'
assert sim.shape == (num_vars, num_vars + 1), f'SIM.shape == [N, N+1] {srname}'
assert np.isfinite(sim).all(), f'SIM is finite {srname}'
assert all(np.max(abs(sim[:, :num_vars]), axis=0) > 0), f'SIM(:, 1:N) has no zero column {srname}'
assert simi.shape == (num_vars, num_vars), f'SIMI.shape == [N, N] {srname}'
assert np.isfinite(simi).all(), f'SIMI is finite {srname}'
assert np.allclose(sim[:, :num_vars] @ simi, np.eye(num_vars), rtol=0.1, atol=0.1) or not all(evaluated), f'SIMI = SIM(:, 1:N)^{-1} {srname}'
return evaluated, conmat, cval, sim, simi, fval, nf, info
def initfilt(conmat, ctol, cweight, cval, fval, sim, evaluated, cfilt, confilt, ffilt, xfilt):
'''
This function initializes the filter (XFILT, etc) that will be used when selecting
x at the end of the solver.
N.B.:
1. Why not initialize the filters using XHIST, etc? Because the history is empty if
the user chooses not to output it.
2. We decouple INITXFC and INITFILT so that it is easier to parallelize the former
if needed.
'''
# Sizes
num_constraints = conmat.shape[0]
num_vars = sim.shape[0]
maxfilt = len(ffilt)
# Preconditions
if DEBUGGING:
assert num_constraints >= 0
assert num_vars >= 1
assert maxfilt >= 1
assert np.size(confilt, 0) == num_constraints and np.size(confilt, 1) == maxfilt
assert np.size(cfilt) == maxfilt
assert np.size(xfilt, 0) == num_vars and np.size(xfilt, 1) == maxfilt
assert np.size(ffilt) == maxfilt
assert np.size(conmat, 0) == num_constraints and np.size(conmat, 1) == num_vars + 1
assert not (np.isnan(conmat) | np.isneginf(conmat)).any()
assert np.size(cval) == num_vars + 1 and not any(cval < 0 | np.isnan(cval) | np.isposinf(cval))
assert np.size(fval) == num_vars + 1 and not any(np.isnan(fval) | np.isposinf(fval))
assert np.size(sim, 0) == num_vars and np.size(sim, 1) == num_vars + 1
assert np.isfinite(sim).all()
assert all(np.max(abs(sim[:, :num_vars]), axis=0) > 0)
assert np.size(evaluated) == num_vars + 1
#====================#
# Calculation starts #
#====================#
nfilt = 0
for i in range(num_vars+1):
if evaluated[i]:
if i < num_vars:
x = sim[:, i] + sim[:, num_vars]
else:
x = sim[:, i] # i == num_vars, i.e. the last column
nfilt, cfilt, ffilt, xfilt, confilt = savefilt(cval[i], ctol, cweight, fval[i], x, nfilt, cfilt, ffilt, xfilt, conmat[:, i], confilt)
#==================#
# Calculation ends #
#==================#
# Postconditions
if DEBUGGING:
assert nfilt <= maxfilt
assert np.size(confilt, 0) == num_constraints and np.size(confilt, 1) == maxfilt
assert not (np.isnan(confilt[:, :nfilt]) | np.isneginf(confilt[:, :nfilt])).any()
assert np.size(cfilt) == maxfilt
assert not any(cfilt[:nfilt] < 0 | np.isnan(cfilt[:nfilt]) | np.isposinf(cfilt[:nfilt]))
assert np.size(xfilt, 0) == num_vars and np.size(xfilt, 1) == maxfilt
assert not (np.isnan(xfilt[:, :nfilt])).any()
# The last calculated X can be Inf (finite + finite can be Inf numerically).
assert np.size(ffilt) == maxfilt
assert not any(np.isnan(ffilt[:nfilt]) | np.isposinf(ffilt[:nfilt]))
return nfilt