| Conditions | 17 |
| Total Lines | 179 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 11 | ||
| Bugs | 1 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like fit_pixel_fixed_scatter() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | #!/usr/bin/env python |
||
| 355 | def fit_pixel_fixed_scatter(flux, ivar, initial_thetas, design_matrix, |
||
| 356 | regularization, censoring_mask, **kwargs): |
||
| 357 | """ |
||
| 358 | Fit theta coefficients and noise residual for a single pixel, using |
||
| 359 | an initially fixed scatter value. |
||
| 360 | |||
| 361 | :param flux: |
||
| 362 | The normalized flux values. |
||
| 363 | |||
| 364 | :param ivar: |
||
| 365 | The inverse variance array for the normalized fluxes. |
||
| 366 | |||
| 367 | :param initial_thetas: |
||
| 368 | A list of initial theta values to start from, and their source. For |
||
| 369 | example: `[(theta_0, "guess"), (theta_1, "old_theta")] |
||
| 370 | |||
| 371 | :param design_matrix: |
||
| 372 | The model design matrix. |
||
| 373 | |||
| 374 | :param regularization: |
||
| 375 | The regularization strength to apply during optimization (Lambda). |
||
| 376 | |||
| 377 | :param censoring_mask: |
||
| 378 | A per-label censoring mask for each pixel. |
||
| 379 | |||
| 380 | :keyword op_method: |
||
| 381 | The optimization method to use. Valid options are: `l_bfgs_b`, `powell`. |
||
| 382 | |||
| 383 | :keyword op_kwds: |
||
| 384 | A dictionary of arguments that will be provided to the optimizer. |
||
| 385 | |||
| 386 | :returns: |
||
| 387 | The optimized theta coefficients, the noise residual `s2`, and |
||
| 388 | metadata related to the optimization process. |
||
| 389 | """ |
||
| 390 | |||
| 391 | if np.sum(ivar) < 1.0 * ivar.size: # MAGIC |
||
| 392 | metadata = dict(message="No pixel information.", op_time=0.0) |
||
| 393 | fiducial = np.hstack([1.0, np.zeros(design_matrix.shape[1] - 1)]) |
||
| 394 | return (fiducial, np.inf, metadata) # MAGIC |
||
| 395 | |||
| 396 | # Determine if any theta coefficients will be censored. |
||
| 397 | censored_theta = ~np.any(np.isfinite(design_matrix), axis=0) |
||
| 398 | # Make the design matrix safe to use. |
||
| 399 | design_matrix[:, censored_theta] = 0 |
||
| 400 | |||
| 401 | feval = [] |
||
| 402 | for initial_theta, initial_theta_source in initial_thetas: |
||
| 403 | feval.append(_pixel_objective_function_fixed_scatter( |
||
| 404 | initial_theta, design_matrix, flux, ivar, regularization, False)) |
||
| 405 | |||
| 406 | initial_theta, initial_theta_source = initial_thetas[np.nanargmin(feval)] |
||
| 407 | |||
| 408 | base_op_kwds = dict(x0=initial_theta, |
||
| 409 | args=(design_matrix, flux, ivar, regularization), |
||
| 410 | disp=False, maxfun=np.inf, maxiter=np.inf) |
||
| 411 | |||
| 412 | theta_0 = kwargs.get("__theta_0", None) |
||
| 413 | if theta_0 is not None: |
||
| 414 | logger.warn("FIXING theta_0. HIGHLY EXPERIMENTAL.") |
||
| 415 | |||
| 416 | # Subtract from flux. |
||
| 417 | # Set design matrix entry to zero. |
||
| 418 | # Update to theta later on. |
||
| 419 | new_flux = flux - theta_0 |
||
| 420 | new_design_matrix = np.copy(design_matrix) |
||
| 421 | new_design_matrix[:, 0] = 0.0 |
||
| 422 | |||
| 423 | base_op_kwds["args"] = (new_design_matrix, new_flux, ivar, regularization) |
||
| 424 | |||
| 425 | if any(censored_theta): |
||
| 426 | # If the initial_theta is the same size as the censored_mask, but different |
||
| 427 | # to the design_matrix, then we need to censor the initial theta so that we |
||
| 428 | # don't bother solving for those parameters. |
||
| 429 | base_op_kwds["x0"] = np.array(base_op_kwds["x0"])[~censored_theta] |
||
| 430 | base_op_kwds["args"] = (design_matrix[:, ~censored_theta], flux, ivar, |
||
| 431 | regularization) |
||
| 432 | |||
| 433 | # Allow either l_bfgs_b or powell |
||
| 434 | t_init = time() |
||
| 435 | default_op_method = "l_bfgs_b" |
||
| 436 | op_method = kwargs.get("op_method", default_op_method) or default_op_method |
||
| 437 | op_method = op_method.lower() |
||
| 438 | |||
| 439 | op_strict = kwargs.get("op_strict", True) |
||
| 440 | |||
| 441 | while True: |
||
| 442 | if op_method == "l_bfgs_b": |
||
| 443 | op_kwds = dict() |
||
| 444 | op_kwds.update(base_op_kwds) |
||
| 445 | op_kwds.update( |
||
| 446 | m=design_matrix.shape[1], maxls=20, factr=10.0, pgtol=1e-6) |
||
| 447 | op_kwds.update((kwargs.get("op_kwds", {}) or {})) |
||
| 448 | |||
| 449 | # If op_bounds are given and we are censoring some theta terms, then we |
||
| 450 | # will need to adjust which op_bounds we provide. |
||
| 451 | if "bounds" in op_kwds and any(censored_theta): |
||
| 452 | op_kwds["bounds"] = [b for b, is_censored in \ |
||
| 453 | zip(op_kwds["bounds"], censored_theta) if not is_censored] |
||
| 454 | |||
| 455 | # Just-in-time to remove forbidden keywords. |
||
| 456 | _remove_forbidden_op_kwds(op_method, op_kwds) |
||
| 457 | |||
| 458 | op_params, fopt, metadata = op.fmin_l_bfgs_b( |
||
| 459 | _pixel_objective_function_fixed_scatter, |
||
| 460 | fprime=None, approx_grad=None, **op_kwds) |
||
| 461 | |||
| 462 | metadata.update(dict(fopt=fopt)) |
||
| 463 | |||
| 464 | warnflag = metadata.get("warnflag", -1) |
||
| 465 | if warnflag > 0: |
||
| 466 | reason = "too many function evaluations or too many iterations" \ |
||
| 467 | if warnflag == 1 else metadata["task"] |
||
| 468 | logger.warn("Optimization warning (l_bfgs_b): {}".format(reason)) |
||
| 469 | |||
| 470 | if op_strict: |
||
| 471 | # Do optimization again. |
||
| 472 | op_method = "powell" |
||
| 473 | base_op_kwds.update(x0=op_params) |
||
| 474 | else: |
||
| 475 | break |
||
| 476 | |||
| 477 | else: |
||
| 478 | break |
||
| 479 | |||
| 480 | elif op_method == "powell": |
||
| 481 | op_kwds = dict() |
||
| 482 | op_kwds.update(base_op_kwds) |
||
| 483 | op_kwds.update(xtol=1e-6, ftol=1e-6) |
||
| 484 | op_kwds.update((kwargs.get("op_kwds", {}) or {})) |
||
| 485 | |||
| 486 | # Set 'False' in args so that we don't return the gradient, |
||
| 487 | # because fmin doesn't want it. |
||
| 488 | args = list(op_kwds["args"]) |
||
| 489 | args.append(False) |
||
| 490 | op_kwds["args"] = tuple(args) |
||
| 491 | |||
| 492 | t_init = time() |
||
| 493 | |||
| 494 | # Just-in-time to remove forbidden keywords. |
||
| 495 | _remove_forbidden_op_kwds(op_method, op_kwds) |
||
| 496 | |||
| 497 | op_params, fopt, direc, n_iter, n_funcs, warnflag = op.fmin_powell( |
||
| 498 | _pixel_objective_function_fixed_scatter, |
||
| 499 | full_output=True, **op_kwds) |
||
| 500 | |||
| 501 | metadata = dict(fopt=fopt, direc=direc, n_iter=n_iter, |
||
| 502 | n_funcs=n_funcs, warnflag=warnflag) |
||
| 503 | break |
||
| 504 | |||
| 505 | else: |
||
| 506 | raise ValueError("unknown optimization method '{}' -- " |
||
| 507 | "powell or l_bfgs_b are available".format(op_method)) |
||
| 508 | |||
| 509 | # Additional metadata common to both optimizers. |
||
| 510 | metadata.update(dict(op_method=op_method, op_time=time() - t_init, |
||
| 511 | initial_theta=initial_theta, initial_theta_source=initial_theta_source)) |
||
| 512 | |||
| 513 | # De-censor the optimized parameters. |
||
| 514 | if any(censored_theta): |
||
| 515 | theta = np.zeros(censored_theta.size) |
||
| 516 | theta[~censored_theta] = op_params |
||
| 517 | |||
| 518 | else: |
||
| 519 | theta = op_params |
||
| 520 | |||
| 521 | if theta_0 is not None: |
||
| 522 | theta[0] = theta_0 |
||
| 523 | |||
| 524 | # Fit the scatter. |
||
| 525 | op_fmin_kwds = dict(disp=False, maxiter=np.inf, maxfun=np.inf) |
||
| 526 | op_fmin_kwds.update( |
||
| 527 | xtol=op_kwds.get("xtol", 1e-8), ftol=op_kwds.get("ftol", 1e-8)) |
||
| 528 | |||
| 529 | residuals_squared = (flux - np.dot(theta, design_matrix.T))**2 |
||
| 530 | scatter = op.fmin(_scatter_objective_function, 0.0, |
||
| 531 | args=(residuals_squared, ivar), disp=False) |
||
| 532 | |||
| 533 | return (theta, scatter**2, metadata) |
||
| 534 |