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 |