• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LSDOlab / modopt / 14892142162

07 May 2025 07:58PM UTC coverage: 82.32% (+0.02%) from 82.301%
14892142162

push

github

anugrahjo
Fix Callable import missing for NelderMead

1 of 1 new or added line in 1 file covered. (100.0%)

6 existing lines in 4 files now uncovered.

5387 of 6544 relevant lines covered (82.32%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.06
/modopt/external_libraries/cobyqa/cobyqa.py
1
import numpy as np
1✔
2
from scipy.optimize import Bounds, LinearConstraint, NonlinearConstraint #, minimize
1✔
3
import time
1✔
4
from modopt.utils.options_dictionary import OptionsDictionary
1✔
5
from modopt import Optimizer
1✔
6
from typing import Callable
1✔
7

8
class COBYQA(Optimizer):
1✔
9
    ''' 
10
    Class that interfaces modOpt with the COBYQA optimization algorithm.
11
    Constrained Optimization BY Quadratic Approximations or COBYQA is a gradient-free optimization algorithm.
12
    Unlike COBYLA, COBYQA also supports equality constraints.
13

14
    Parameters
15
    ----------
16
    problem : Problem or ProblemLite
17
        Object containing the problem to be solved.
18
    recording : bool, default=False
19
        If ``True``, record all outputs from the optimization.
20
        This needs to be enabled for hot-starting the same problem later,
21
        if the optimization is interrupted.
22
    hot_start_from : str, optional
23
        The record file from which to hot-start the optimization.
24
    hot_start_atol : float, default=0.
25
        The absolute tolerance check for the inputs
26
        when reusing outputs from the hot-start record.
27
    hot_start_rtol : float, default=0.
28
        The relative tolerance check for the inputs
29
        when reusing outputs from the hot-start record.
30
    visualize : list, default=[]
31
        The list of scalar variables to visualize during the optimization.
32
    keep_viz_open : bool, default=False
33
        If ``True``, keep the visualization window open after the optimization is complete.
34
    turn_off_outputs : bool, default=False
35
        If ``True``, prevent modOpt from generating any output files.
36

37
    solver_options : dict, default={}
38
        Dictionary containing the options to be passed to the solver.
39
        Available options are: 'maxfev', 'maxiter', 'target', 'feasibility_tol',
40
        'radius_init', 'radius_final', 'nb_points', 'scale', 'filter_size',
41
        'store_history', 'history_size', 'debug', 'disp', 'callback'.
42
        See the COBYQA page in modOpt's documentation for more information.
43
    readable_outputs : list, default=[]
44
        List of outputs to be written to readable text output files.
45
        Available outputs are: 'x', 'obj'.
46
    '''
47
    def initialize(self):
1✔
48
        '''
49
        Initialize the optimizer.
50
        Declare options, solver_options and outputs.
51
        '''
52
        # Declare options
53
        self.solver_name = 'cobyqa'
1✔
54
        self.options.declare('solver_options', types=dict, default={})
1✔
55
        NPT = int(2*self.problem.nx+1)
1✔
56
        self.default_solver_options = {
1✔
57
            'maxfev': (int, 500),               # Maximum number of function evaluations (default: 500 * len(x0))
58
            'maxiter': (int, 1000),             # Maximum number of iterations (default: 1000 * len(x0))
59
            'target': (float, -np.inf),         # Target objective function value
60
            'feasibility_tol': (float, 1e-8),   # Tolerance on constraint violations
61
            'radius_init': (float, 1.0),        # Initial trust region radius
62
            'radius_final': (float, 1e-6),      # Final trust region radius
63
            'nb_points': (int, NPT),            # Number of interpolation points used to build the quadratic model of f and c, 0<NPT<=(n+1)*(n+2)//2
64
            'scale': (bool, False),             # Whether to scale the variables according to the bounds
65
            'filter_size': (int, int(1e6)),     # Maximum number of points in the filter. The filter is used to store the best point returned by the algorithm
66
            'store_history': (bool, False),     # Whether to store the history of the function evaluations
67
            'history_size': (int, int(1e6)),    # Maximum number of function evaluations to store in the history
68
            'debug': (bool, False),             # To perform additional checks during the optimization procedure.
69
            'disp': (bool, False),
70
            'callback': ((type(None), Callable), None),
71
        }
72

73
        # Used for verifying the keys and value-types of user-provided solver_options
74
        self.solver_options = OptionsDictionary()
1✔
75
        for key, value in self.default_solver_options.items():
1✔
76
            self.solver_options.declare(key, types=value[0], default=value[1])
1✔
77

78
        # Declare outputs
79
        self.available_outputs = {
1✔
80
            'x'  : (float, (self.problem.nx,)),
81
            'obj': float
82
            }
83
        self.options.declare('readable_outputs', values=([], ['x'], ['obj'], ['x', 'obj']), default=[])
1✔
84

85
        # Define the initial guess, objective, constraints
86
        self.x0   = self.problem.x0 * 1.0
1✔
87
        self.obj  = self.problem._compute_objective
1✔
88
        self.active_callbacks = ['obj']
1✔
89
        if self.problem.constrained:
1✔
90
            self.con  = self.problem._compute_constraints
1✔
91
            self.active_callbacks += ['con']
1✔
92

93
    def setup(self):
1✔
94
        '''
95
        Setup the optimizer.
96
        Setup outputs, bounds, and constraints.
97
        Check the validity of user-provided 'solver_options'.
98
        '''
99
        # Check if user-provided solver_options have valid keys and value-types
100
        self.solver_options.update(self.options['solver_options'])
1✔
101
        self.options_to_pass = self.solver_options.get_pure_dict()
1✔
102
        self.user_callback = self.options_to_pass.pop('callback')
1✔
103
        # Adapt bounds as scipy Bounds() object
104
        self.setup_bounds()
1✔
105

106
        # Adapt constraints as a list of dictionaries with constraints = 0 or >= 0
107
        if self.problem.constrained:
1✔
108
            self.setup_constraints()
1✔
109
        else:
UNCOV
110
            self.constraints = ()
×
111

112
    def setup_bounds(self):
1✔
113
        '''
114
        Adapt bounds as a Scipy Bounds() object.
115
        Only for Nelder-Mead, L-BFGS-B, TNC, SLSQP, Powell, trust-constr, COBYLA, and COBYQA methods.
116
        '''
117
        xl = self.problem.x_lower
1✔
118
        xu = self.problem.x_upper
1✔
119

120
        if np.all(xl == -np.inf) and np.all(xu == np.inf):
1✔
UNCOV
121
            self.bounds = None
×
122
        else:
123
            self.bounds = Bounds(xl, xu, keep_feasible=False)
1✔
124
            # OR the following can also be used
125
            # self.bounds = np.array([xl, xu]).T
126

127
    def setup_constraints(self):
1✔
128
        '''
129
        Adapt constraints as a a single/list of scipy LinearConstraint() or NonlinearConstraint() objects.
130
        '''
131
        cl = self.problem.c_lower
1✔
132
        cu = self.problem.c_upper
1✔
133
        self.constraints = NonlinearConstraint(self.con, cl, cu)
1✔
134

135
    def solve(self):
1✔
136

137
        try:
1✔
138
            # import latest cobyqa if available
139
            from cobyqa import minimize
1✔
140
        except ImportError:
1✔
141
            # else access cobyqa installed with Scipy>=1.14.0 (requires python>=3.10)
142
            try:
1✔
143
                from scipy._lib.cobyqa import minimize
1✔
144
            except ImportError:
×
UNCOV
145
                raise ImportError("'cobyqa' could not be imported. Install cobyqa using 'pip install cobyqa' for using COBYQA optimizer.")
×
146

147
        def callback(intermediate_result): 
1✔
148
            x = intermediate_result['x']
1✔
149
            f = intermediate_result['fun']
1✔
150
            self.update_outputs(x=x, obj=f)
1✔
151
            if self.user_callback: self.user_callback(x, f)
1✔
152

153
        # self.update_outputs(x=self.x0, obj=self.obj(self.x0)) # maybe required for scipy.optimize.minimize
154

155
        # Call the cobyqa algorithm (not from Scipy)
156
        start_time = time.time()
1✔
157
        self.results = minimize(
1✔
158
            self.obj,
159
            self.x0,
160
            args=(),
161
            # method='COBYQA',  # for scipy.optimize.minimize
162
            # jac=None,         # for scipy.optimize.minimize
163
            # hess=None,        # for scipy.optimize.minimize
164
            # hessp=None,       # for scipy.optimize.minimize
165
            bounds=self.bounds,
166
            constraints=self.constraints,
167
            # tol=None,         # for scipy.optimize.minimize
168
            callback=callback,
169
            options=self.options_to_pass
170
            )
171
        self.total_time = time.time() - start_time
1✔
172

173
        self.run_post_processing()
1✔
174

175
        return self.results
1✔
176
    
177
    def print_results(self, 
1✔
178
                      optimal_variables=False,
179
                      obj_history=False,
180
                      max_con_viol_history=False,
181
                      all=False):
182
        '''
183
        Print the optimization results to the console.
184

185
        Parameters
186
        ----------
187
        optimal_variables : bool, default=False
188
            If ``True``, print the optimal variables.
189
        obj_history : bool, default=False
190
            If ``True``, print the objective history.
191
        max_con_viol_history : bool, default=False
192
            If ``True``, print the maximum constraint violation history.
193
        all : bool, default=False
194
            If ``True``, print all available information.
195
        '''
196
        output  = "\n\tSolution from COBYQA:"
1✔
197
        output += "\n\t"+"-" * 100
1✔
198

199
        output += f"\n\t{'Problem':25}: {self.problem_name}"
1✔
200
        output += f"\n\t{'Solver':25}: {self.solver_name}"
1✔
201
        output += f"\n\t{'Success':25}: {self.results['success']}"
1✔
202
        output += f"\n\t{'Message':25}: {self.results['message']}"
1✔
203
        output += f"\n\t{'Status':25}: {self.results['status']}"
1✔
204
        output += f"\n\t{'Total time':25}: {self.total_time}"
1✔
205
        output += f"\n\t{'Objective':25}: {self.results['fun']}"
1✔
206
        output += f"\n\t{'Max. constraint violation':25}: {self.results['maxcv']}"
1✔
207
        output += f"\n\t{'Total function evals':25}: {self.results['nfev']}"
1✔
208
        output += f"\n\t{'Total iterations':25}: {self.results['nit']}"
1✔
209
        output += self.get_callback_counts_string(25)
1✔
210

211
        if optimal_variables or all:
1✔
212
            output += f"\n\t{'Optimal variables':25}: {self.results['x']}"
1✔
213
        if (obj_history or all) and self.solver_options['store_history']:
1✔
214
            output += f"\n\t{'Objective history':25}: {self.results['fun_history']}"
1✔
215
        if (max_con_viol_history or all) and self.solver_options['store_history'] and self.problem.constrained:
1✔
216
            output += f"\n\t{'Max. con. viol. history':25}: {self.results['maxcv_history']}"
1✔
217

218
        output += '\n\t' + '-'*100
1✔
219
        print(output)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc