215 lines
9.2 KiB
Python
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
|