Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-Prune Constraints with None as the Upper and Lower Bound #3155

Open
andrewellis55 opened this issue Mar 20, 2024 · 3 comments
Open

Auto-Prune Constraints with None as the Upper and Lower Bound #3155

andrewellis55 opened this issue Mar 20, 2024 · 3 comments

Comments

@andrewellis55
Copy link
Contributor

Desired capability or behavior.

In my project, many constraints are user settable. This might look like

class Comp(om.ExplicitCompoenent):
    def initialize(self):
        self.declare('con1_upper', default=None)
        self.declare('con1_lower', default=None)

    def setup(self):
        ....
        self.add_constraint('con_1', lower=self.options['con1_lower'], upper=self.options['con1_upper'])

Is it possible to add an auto-prune feature that removes any constraints were the upper and lower are None? This avoids either a lot of if statements before each declaration or needlessly enlarging the Jacobean

Associated POEM

No response

@andrewellis55
Copy link
Contributor Author

andrewellis55 commented Mar 28, 2024

@robfalck I can make a PR for this one if this seems reasonable

Alternatively, I've been thinking about another possible implementation here where a parameter ignore_unbounded_constraints=True could be added to add_constraint that could look like the following

    def add_constraint(self, name, lower=None, upper=None, equals=None,
                       ref=None, ref0=None, adder=None, scaler=None, units=None,
                       indices=None, linear=False, parallel_deriv_color=None,
                       cache_linear_solution=False, flat_indices=False, alias=None,
                       ignore_unbounded_constraints=True):

        if ((upper is not None) or (lower is not None) or (equals is not None) or (ignore_unbounded_constraints is False):
            self.add_response(name=name, type_='con', lower=lower, upper=upper,
                              equals=equals, scaler=scaler, adder=adder, ref=ref,
                              ref0=ref0, indices=indices, linear=linear, units=units,
                              parallel_deriv_color=parallel_deriv_color,
                              cache_linear_solution=cache_linear_solution,
                              flat_indices=flat_indices, alias=alias)

I can write up a PEOM if need, just seemed like a relatively small change.

Thanks

@robfalck
Copy link
Contributor

I'm of a mind that any constraint where upper, lower, and equals are all None, then the constraint should just be ignored. I can't think of a valid reason to do it, but that would potentially break backward compatibility for some people.

I think the ignore_unbounded_constraint should be an option of Driver.

@andrewellis55
Copy link
Contributor Author

andrewellis55 commented Mar 28, 2024

My only reason for including ignore_unbounded_constraints was for backwards compatibility. Its not needed for my application. I'll add in the backwards compatibility as well

Would something like this make sense if added to driver.py? This seems to work for me

  def _update_voi_meta(self, model):

      self._objs = objs = {}
      self._cons = cons = {}

      model._setup_driver_units()

      def con_is_unbounded(con):
          unbounded_values = [None, 1e30, -1e30]
          con_types = ['upper', 'lower', 'equals']
          is_unbounded = all(con[con_type] in unbounded_values for con_type in con_types)
          return is_unbounded

      self._responses = resps = model.get_responses(recurse=True, use_prom_ivc=True)
      for name, data in resps.items():
          if data['type'] == 'con':
              if not con_is_unbounded(data):
                  cons[name] = data
          else:
              objs[name] = data

      response_size = sum(resps[n]['global_size'] for n in self._get_ordered_nl_responses())

      # Gather up the information for design vars.
      self._designvars = designvars = model.get_design_vars(recurse=True, use_prom_ivc=True)
      desvar_size = sum(data['global_size'] for data in designvars.values())

      return response_size, desvar_size

@robfalck robfalck removed their assignment Apr 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants