| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- '''
- 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
|