psf / black

The uncompromising Python code formatter
https://black.readthedocs.io/en/stable/
MIT License
38.81k stars 2.45k forks source link

INTERNAL ERROR: Black produced different code on the second pass of the formatter. #1903

Closed Viech closed 3 years ago

Viech commented 3 years ago

File is found here, formatting with black -l 80, version is 20.8b1.

Mode(target_versions=set(), line_length=80, string_normalization=True, experimental_string_processing=False, is_pyi=False)
--- source
+++ first pass
@@ -76,14 +76,14 @@

     #: Short string denoting a feasibility problem.
     FIND = "find"

     #: Short string denoting a minimization problem.
-    MIN  = "min"
+    MIN = "min"

     #: Short string denoting a maximization problem.
-    MAX  = "max"
+    MAX = "max"

     def __init__(self, direction=None, function=None):
         """Construct an optimization objective.

         :param str direction:
@@ -110,31 +110,37 @@
                 direction = self.MIN
             elif lower.startswith("max"):
                 direction = self.MAX
             else:
                 raise ValueError(
-                    "Invalid search direction '{}'.".format(direction))
+                    "Invalid search direction '{}'.".format(direction)
+                )

         if function is None:
             if direction != self.FIND:
                 raise ValueError("Missing an objective function.")
         else:
             if direction == self.FIND:
-                raise ValueError("May not specify an objective function for a "
-                    "feasiblity problem.")
+                raise ValueError(
+                    "May not specify an objective function for a "
+                    "feasiblity problem."
+                )

             if not isinstance(function, expressions.Expression):
                 raise TypeError(
-                    "Objective function must be a PICOS expression.")
+                    "Objective function must be a PICOS expression."
+                )

             if len(function) != 1:
                 raise TypeError("Objective function must be scalar.")

             function = function.refined

-            if isinstance(function, expressions.ComplexAffineExpression) \
-            and function.complex:
+            if (
+                isinstance(function, expressions.ComplexAffineExpression)
+                and function.complex
+            ):
                 raise TypeError("Objective function may not be complex.")

         self._direction = direction
         self._function = function

@@ -145,11 +151,12 @@
             minimize = self._direction == self.MIN
             dir_str = "minimize" if minimize else "maximize"

             if self._function.uncertain:
                 obj_str = self._function.worst_case_string(
-                    "max" if minimize else "min")
+                    "max" if minimize else "min"
+                )
             else:
                 obj_str = self._function.string

             return "{} {}".format(dir_str, obj_str)

@@ -233,56 +240,71 @@
                 bad_direction = self.FIND

             try:
                 return self._function.worst_case_value(bad_direction)
             except IntractableWorstCase as error:
-                raise IntractableWorstCase("Failed to compute the worst-case "
+                raise IntractableWorstCase(
+                    "Failed to compute the worst-case "
                     "value of the objective function {}: {} Maybe evaluate the "
-                    "nominal objective function instead?"
-                    .format(self._function.string, error))
+                    "nominal objective function instead?".format(
+                        self._function.string, error
+                    )
+                )
         else:
             return self._function.value

     def __index__(self):
         if self._function is None:
-            raise TypeError("A feasiblity objective cannot be used as an index "
-                "because there is no objective function to take the value of.")
+            raise TypeError(
+                "A feasiblity objective cannot be used as an index "
+                "because there is no objective function to take the value of."
+            )

         value = self.value

         if value is None:
             raise expressions.NotValued(
-                "Cannot use unvalued objective function {} as an index."
-                .format(self._function.string))
+                "Cannot use unvalued objective function {} as an index.".format(
+                    self._function.string
+                )
+            )

         try:
             fltValue = float(value)
             intValue = int(fltValue)

             if intValue != fltValue:
                 raise ValueError
         except (TypeError, ValueError):
-            raise RuntimeError("Cannot use the objective function {} as an "
-                "index as its value of {} is not integral."
-                .format(self._function.string, value))
+            raise RuntimeError(
+                "Cannot use the objective function {} as an "
+                "index as its value of {} is not integral.".format(
+                    self._function.string, value
+                )
+            )

         return intValue

     def _casting_helper(self, theType):
         assert theType in (int, float, complex)

         if self._function is None:
-            raise TypeError("A feasiblity objective cannot be cast as {} "
-                "because there is no objective function to take the value of."
-                .format(theType.__name__))
+            raise TypeError(
+                "A feasiblity objective cannot be cast as {} "
+                "because there is no objective function to take the value of.".format(
+                    theType.__name__
+                )
+            )

         value = self.value

         if value is None:
             raise expressions.NotValued(
-                "Cannot cast unvalued objective function {} as {}."
-                .format(self._function.string, theType.__name__))
+                "Cannot cast unvalued objective function {} as {}.".format(
+                    self._function.string, theType.__name__
+                )
+            )

         return theType(value)

     def __int__(self):
         return self._casting_helper(int)
@@ -295,11 +317,11 @@

     def __round__(self, ndigits=None):
         return round(float(self), ndigits)

-class Problem():
+class Problem:
     """PICOS' representation of an optimization problem.

     :Example:

     >>> from picos import Problem, RealVariable
@@ -332,14 +354,19 @@
     """

     #: The specification for problems returned by :meth:`conic_form`.
     CONIC_FORM = Specification(
         objectives=[expressions.AffineExpression],
-        constraints=[C for C in
-            (getattr(constraints, Cname) for Cname in constraints.__all__)
+        constraints=[
+            C
+            for C in (
+                getattr(constraints, Cname) for Cname in constraints.__all__
+            )
             if issubclass(C, constraints.ConicConstraint)
-            and C is not constraints.ConicConstraint])
+            and C is not constraints.ConicConstraint
+        ],
+    )

     # --------------------------------------------------------------------------
     # Initialization and reset methods.
     # --------------------------------------------------------------------------

@@ -357,11 +384,12 @@
             A sequence of additional solver options to apply on top of the
             default options or those given by ``copyOptions`` or ``useOptions``.
         """
         if copyOptions and useOptions:
             raise ValueError(
-                "Can only copy or use existing solver options, not both.")
+                "Can only copy or use existing solver options, not both."
+            )

         extra_options = map_legacy_options(**extra_options)

         if copyOptions:
             self._options = copyOptions.copy()
@@ -550,12 +578,14 @@
         return self._options

     @options.setter
     def options(self, value):
         if not isinstance(value, Options):
-            raise TypeError("Cannot assign an object of type {} as a problem's "
-                " options.".format(type(value).__name__))
+            raise TypeError(
+                "Cannot assign an object of type {} as a problem's "
+                " options.".format(type(value).__name__)
+            )

         self._options = value

     @options.deleter
     def options(self, value):
@@ -615,16 +645,20 @@
     def strategy(self, value):
         from .strategy import Strategy

         if not isinstance(value, Strategy):
             raise TypeError(
-                "Cannot assign an object of type {} as a solution strategy."
-                .format(type(value).__name__))
+                "Cannot assign an object of type {} as a solution strategy.".format(
+                    type(value).__name__
+                )
+            )

         if value.problem is not self:
-            raise ValueError("The solution strategy was constructed for a "
-                "different problem.")
+            raise ValueError(
+                "The solution strategy was constructed for a "
+                "different problem."
+            )

         self._strategy = value

     @strategy.deleter
     def strategy(self):
@@ -651,18 +685,20 @@
     @property
     def continuous(self):
         """Whether all variables are of continuous types."""
         return all(
             isinstance(variable, expressions.CONTINUOUS_VARTYPES)
-            for variable in self._variables.values())
+            for variable in self._variables.values()
+        )

     @property
     def pure_integer(self):
         """Whether all variables are of integral types."""
         return not any(
             isinstance(variable, expressions.CONTINUOUS_VARTYPES)
-            for variable in self._variables.values())
+            for variable in self._variables.values()
+        )

     @property
     def type(self):
         """The problem type as a string, such as "Linear Program"."""
         C = set(type(c) for c in self._constraints.values())
@@ -672,30 +708,30 @@
         linear = [
             constraints.AffineConstraint,
             constraints.ComplexAffineConstraint,
             constraints.AbsoluteValueConstraint,
             constraints.SimplexConstraint,
-            constraints.FlowConstraint]
-        sdp = [
-            constraints.LMIConstraint,
-            constraints.ComplexLMIConstraint]
+            constraints.FlowConstraint,
+        ]
+        sdp = [constraints.LMIConstraint, constraints.ComplexLMIConstraint]
         quadratic = [
             constraints.ConvexQuadraticConstraint,
             constraints.ConicQuadraticConstraint,
-            constraints.NonconvexQuadraticConstraint]
-        quadconic = [
-            constraints.SOCConstraint,
-            constraints.RSOCConstraint]
+            constraints.NonconvexQuadraticConstraint,
+        ]
+        quadconic = [constraints.SOCConstraint, constraints.RSOCConstraint]
         exponential = [
             constraints.ExpConeConstraint,
             constraints.SumExponentialsConstraint,
             constraints.LogSumExpConstraint,
             constraints.LogConstraint,
-            constraints.KullbackLeiblerConstraint]
+            constraints.KullbackLeiblerConstraint,
+        ]
         complex = [
             constraints.ComplexAffineConstraint,
-            constraints.ComplexLMIConstraint]
+            constraints.ComplexLMIConstraint,
+        ]

         if objective is None:
             if not C:
                 base = "Empty Problem"
             elif C.issubset(set(linear)):
@@ -767,10 +803,11 @@
             This property is intended for educational purposes.
             If you want to solve the primal problem via its dual, use the
             :ref:`dualize <option_dualize>` option instead.
         """
         from ..reforms import Dualization
+
         return self.reformulated(Dualization.SUPPORTED, dualize=True)

     @property
     def conic_form(self):
         """The problem in conic form.
@@ -859,14 +896,18 @@
         if len(group) == 1:
             return group[0].long_string

         try:
             template, data = parameterized_string(
-                [mtb.long_string for mtb in group])
+                [mtb.long_string for mtb in group]
+            )
         except ValueError:
-            return group[0].long_string \
-                + ", " + ", ".join([v.name for v in group[1:]])
+            return (
+                group[0].long_string
+                + ", "
+                + ", ".join([v.name for v in group[1:]])
+            )
         else:
             return glyphs.forall(template, data)

     @lru_cache()
     def _con_group_string(self, group):
@@ -893,19 +934,21 @@

         # Print objective.
         string += "  {}\n".format(self._objective)

         wrapper = TextWrapper(
-            initial_indent=" "*4,
-            subsequent_indent=" "*6,
+            initial_indent=" " * 4,
+            subsequent_indent=" " * 6,
             break_long_words=False,
-            break_on_hyphens=False)
+            break_on_hyphens=False,
+        )

         # Print variables.
         if self._variables:
             string += "  {}\n".format(
-                "for" if self._objective.direction == "find" else "over")
+                "for" if self._objective.direction == "find" else "over"
+            )
             for group in self._var_groups:
                 string += wrapper.fill(self._mtb_group_string(tuple(group)))
                 string += "\n"

         # Print constraints.
@@ -945,34 +988,46 @@

     def _register_mutables(self, mtbs):
         """Register the mutables of an objective function or constraint."""
         # Register every mutable at most once per call.
         if not isinstance(mtbs, (set, frozenset)):
-            raise TypeError("Mutable registry can (un)register a mutable "
-                "only once per call, so the argument must be a set type.")
+            raise TypeError(
+                "Mutable registry can (un)register a mutable "
+                "only once per call, so the argument must be a set type."
+            )

         # Retrieve old and new mutables as mapping from name to object.
         old_mtbs = self._mutables
         new_mtbs = OrderedDict(
-            (mtb.name, mtb) for mtb in sorted(mtbs, key=(lambda m: m.name)))
-        new_vars = OrderedDict((name, mtb) for name, mtb in new_mtbs.items()
-            if isinstance(mtb, BaseVariable))
-        new_prms = OrderedDict((name, mtb) for name, mtb in new_mtbs.items()
-            if not isinstance(mtb, BaseVariable))
+            (mtb.name, mtb) for mtb in sorted(mtbs, key=(lambda m: m.name))
+        )
+        new_vars = OrderedDict(
+            (name, mtb)
+            for name, mtb in new_mtbs.items()
+            if isinstance(mtb, BaseVariable)
+        )
+        new_prms = OrderedDict(
+            (name, mtb)
+            for name, mtb in new_mtbs.items()
+            if not isinstance(mtb, BaseVariable)
+        )

         # Check for mutable name clashes within the new set.
         if len(new_mtbs) != len(mtbs):
             raise ValueError(
                 "The object you are trying to add to a problem contains "
-                "multiple mutables of the same name. This is not allowed.")
+                "multiple mutables of the same name. This is not allowed."
+            )

         # Check for mutable name clashes with existing mutables.
         for name in set(old_mtbs).intersection(set(new_mtbs)):
             if old_mtbs[name] is not new_mtbs[name]:
-                raise ValueError("Cannot register the mutable {} with the "
+                raise ValueError(
+                    "Cannot register the mutable {} with the "
                     "problem because it already tracks another mutable with "
-                    "the same name.".format(name))
+                    "the same name.".format(name)
+                )

         # Keep track of new mutables.
         self._mutables.update(new_mtbs)
         self._variables.update(new_vars)
         self._parameters.update(new_prms)
@@ -984,21 +1039,25 @@

     def _unregister_mutables(self, mtbs):
         """Unregister the mutables of an objective function or constraint."""
         # Unregister every mutable at most once per call.
         if not isinstance(mtbs, (set, frozenset)):
-            raise TypeError("Mutable registry can (un)register a mutable "
-                "only once per call, so the argument must be a set type.")
+            raise TypeError(
+                "Mutable registry can (un)register a mutable "
+                "only once per call, so the argument must be a set type."
+            )

         for mtb in mtbs:
             name = mtb.name

             # Make sure the mutable is properly registered.
-            assert name in self._mutables and mtb in self._mtb_count, \
-                "Tried to unregister a mutable that is not registered."
-            assert self._mtb_count[mtb] >= 1, \
-                "Found a nonpostive mutable count."
+            assert (
+                name in self._mutables and mtb in self._mtb_count
+            ), "Tried to unregister a mutable that is not registered."
+            assert (
+                self._mtb_count[mtb] >= 1
+            ), "Found a nonpostive mutable count."

             # Count down the mutable references.
             self._mtb_count[mtb] -= 1

             # Remove a mutable with a reference count of zero.
@@ -1049,20 +1108,25 @@
             elif idOrIndOrCon < len(self._constraints):
                 # An offset.
                 return list(self._constraints.keys())[idOrIndOrCon]
             else:
                 raise LookupError(
-                    "The problem has no constraint with ID or offset {}."
-                    .format(idOrIndOrCon))
+                    "The problem has no constraint with ID or offset {}.".format(
+                        idOrIndOrCon
+                    )
+                )
         elif isinstance(idOrIndOrCon, constraints.Constraint):
             # A constraint object.
             id = idOrIndOrCon.id
             if id in self._constraints:
                 return id
             else:
-                raise KeyError("The constraint '{}' is not part of the problem."
-                    .format(idOrIndOrCon))
+                raise KeyError(
+                    "The constraint '{}' is not part of the problem.".format(
+                        idOrIndOrCon
+                    )
+                )
         elif isinstance(idOrIndOrCon, tuple) or isinstance(idOrIndOrCon, list):
             if len(idOrIndOrCon) == 1:
                 groupIndex = idOrIndOrCon[0]
                 if groupIndex < len(self._con_groups):
                     return [c.id for c in self._con_groups[groupIndex]]
@@ -1074,19 +1138,24 @@
                     group = self._con_groups[groupIndex]
                     if groupOffset < len(group):
                         return group[groupOffset].id
                     else:
                         raise IndexError(
-                            "Constraint group offset out of range.")
+                            "Constraint group offset out of range."
+                        )
                 else:
                     raise IndexError("Constraint group index out of range.")
             else:
-                raise TypeError("If looking up constraints by group, the index "
-                    "must be a tuple or list of length at most two.")
+                raise TypeError(
+                    "If looking up constraints by group, the index "
+                    "must be a tuple or list of length at most two."
+                )
         else:
-            raise TypeError("Argument of type '{}' not supported when looking "
-                "up constraints".format(type(idOrIndOrCon)))
+            raise TypeError(
+                "Argument of type '{}' not supported when looking "
+                "up constraints".format(type(idOrIndOrCon))
+            )

     def get_constraint(self, idOrIndOrCon):
         """Return a (list of) constraint(s) of the problem.

         :param idOrIndOrCon: One of the following:
@@ -1162,11 +1231,12 @@
         :returns: The constraint that was added to the problem.
         """
         # Handle deprecated 'key' parameter.
         if key is not None:
             throw_deprecation_warning(
-                "Naming constraints is currently not supported.")
+                "Naming constraints is currently not supported."
+            )

         # Register the constraint.
         self._constraints[constraint.id] = constraint
         self._con_groups.append([constraint])

@@ -1223,12 +1293,14 @@
             ‖w[i,j]‖ ≤ y[j] ∀ (i,j) ∈ zip([1,2,4],[2,0,2])
             y[i] ≥ y[i+1] ∀ i ∈ [0…3]
         """
         if it is not None or indices is not None or key is not None:
             # Deprecated as of 2.0.
-            throw_deprecation_warning("Arguments 'it', 'indices' and 'key' to "
-                "add_list_of_constraints are deprecated and ignored.")
+            throw_deprecation_warning(
+                "Arguments 'it', 'indices' and 'key' to "
+                "add_list_of_constraints are deprecated and ignored."
+            )

         added = []
         for constraint in lst:
             added.append(self.add_constraint(constraint))
             self._con_groups.pop()
@@ -1252,12 +1324,14 @@
             for j, candidate in enumerate(group):
                 if candidate is constraint:
                     return i, j

         if constraint in self._constraints.values():
-            raise RuntimeError("The problem's constraint and constraint group "
-                "registries are out of sync.")
+            raise RuntimeError(
+                "The problem's constraint and constraint group "
+                "registries are out of sync."
+            )
         else:
             raise KeyError("The constraint is not part of the problem.")

     def remove_constraint(self, idOrIndOrCon):
         """Delete a constraint from the problem.
@@ -1439,19 +1513,25 @@
                     intParams = sorted([int(p) for p in params])
                 except ValueError:
                     pass
                 else:
                     if intParams == list(range(len(intParams))):
-                        return [self._variables["{}[{}]".format(name, param)]
-                            for param in intParams]
+                        return [
+                            self._variables["{}[{}]".format(name, param)]
+                            for param in intParams
+                        ]

                 # Otherwise return a dict.
-                return {param: self._variables["{}[{}]".format(name, param)]
-                    for param in params}
+                return {
+                    param: self._variables["{}[{}]".format(name, param)]
+                    for param in params
+                }
             else:
-                raise KeyError("The problem references no variable or group of "
-                    "variables named '{}'.".format(name))
+                raise KeyError(
+                    "The problem references no variable or group of "
+                    "variables named '{}'.".format(name)
+                )

     def get_valued_variable(self, name):
         """Retrieve values of variables referenced by the problem.

         This method works the same :meth:`get_variable` but it returns the
@@ -1484,11 +1564,12 @@
         new_mtbs = {mtb: mtb.copy() for name, mtb in self._mutables.items()}

         # Make copies of constraints on top of the new mutables.
         for group in self._con_groups:
             the_copy.add_list_of_constraints(
-                constraint.replace_mutables(new_mtbs) for constraint in group)
+                constraint.replace_mutables(new_mtbs) for constraint in group
+            )

         # Make a copy of the objective on top of the new mutables.
         direction, function = self._objective
         if function is not None:
             the_copy.objective = direction, function.replace_mutables(new_mtbs)
@@ -1510,11 +1591,12 @@
         # Relax integral variables and copy other mutables if requested.
         new_mtbs = {}
         for name, var in self._mutables.items():
             if isinstance(var, expressions.IntegerVariable):
                 new_mtbs[name] = expressions.RealVariable(
-                    name, var.shape, var._lower, var._upper)
+                    name, var.shape, var._lower, var._upper
+                )
             elif isinstance(var, expressions.BinaryVariable):
                 new_mtbs[name] = expressions.RealVariable(name, var.shape, 0, 1)
             else:
                 if copy_other_mutables:
                     new_mtbs[name] = var.copy()
@@ -1522,11 +1604,12 @@
                     new_mtbs[name] = var

         # Make copies of constraints on top of the new mutables.
         for group in self._con_groups:
             the_copy.add_list_of_constraints(
-                constraint.replace_mutables(new_mtbs) for constraint in group)
+                constraint.replace_mutables(new_mtbs) for constraint in group
+            )

         # Make a copy of the objective on top of the new mutables.
         direction, function = self._objective
         if function is not None:
             the_copy.objective = direction, function.replace_mutables(new_mtbs)
@@ -1658,19 +1741,27 @@
             steps = numReforms

         if steps == 0:
             return self
         elif steps > numReforms:
-            raise ValueError("The pipeline {} has only {} reformulation steps "
-                "to choose from.".format(strategy, numReforms))
+            raise ValueError(
+                "The pipeline {} has only {} reformulation steps "
+                "to choose from.".format(strategy, numReforms)
+            )

         # Replace the successor of the last reformulation with a dummy solver.
         lastReform = strategy.reforms[steps - 1]
         oldSuccessor = lastReform.successor
-        lastReform.successor = type("DummySolver", (), {
-            "execute": lambda self: Solution(
-                {}, solver="dummy", vectorizedPrimals=True)})()
+        lastReform.successor = type(
+            "DummySolver",
+            (),
+            {
+                "execute": lambda self: Solution(
+                    {}, solver="dummy", vectorizedPrimals=True
+                )
+            },
+        )()

         # Execute the cut-short strategy.
         strategy.execute(**extra_options)

         # Repair the last reformulation.
@@ -1742,38 +1833,51 @@
             This method is intended for educational purposes.
             You do not need to use it when solving a problem as PICOS will
             perform the necessary reformulations automatically.
         """
         if not isinstance(specification, Specification):
-            raise TypeError("The desired problem type must be given as a "
-                "Specification object.")
+            raise TypeError(
+                "The desired problem type must be given as a "
+                "Specification object."
+            )

         # Create a placeholder function for abstract methods of a dummy solver.
         def placeholder(the_self):
-            raise RuntimeError("The dummy solver created by "
-                "Problem.reformulated must not be executed.")
+            raise RuntimeError(
+                "The dummy solver created by "
+                "Problem.reformulated must not be executed."
+            )

         # Declare a dummy solver that accepts specified problems.
-        DummySolver = type("DummySolver", (Solver,), {
-            # Abstract class methods.
-            "supports": classmethod(lambda cls, footprint:
-                Solver.supports(footprint) and footprint in specification),
-            "default_penalty": classmethod(lambda cls: 0),
-            "test_availability": classmethod(lambda cls: None),
-            "names": classmethod(lambda cls: ("Dummy Solver", "DummySolver",
-                "Dummy Solver accepting {}".format(specification))),
-            "is_free": classmethod(lambda cls: True),
-
-            # Additional class methods needed for an ad-hoc solver.
-            "penalty": classmethod(lambda cls, options: 0),
-
-            # Abstract instance methods.
-            "reset_problem": lambda self: placeholder(self),
-            "_import_problem": lambda self: placeholder(self),
-            "_update_problem": lambda self: placeholder(self),
-            "_solve": lambda self: placeholder(self)
-        })
+        DummySolver = type(
+            "DummySolver",
+            (Solver,),
+            {
+                # Abstract class methods.
+                "supports": classmethod(
+                    lambda cls, footprint: Solver.supports(footprint)
+                    and footprint in specification
+                ),
+                "default_penalty": classmethod(lambda cls: 0),
+                "test_availability": classmethod(lambda cls: None),
+                "names": classmethod(
+                    lambda cls: (
+                        "Dummy Solver",
+                        "DummySolver",
+                        "Dummy Solver accepting {}".format(specification),
+                    )
+                ),
+                "is_free": classmethod(lambda cls: True),
+                # Additional class methods needed for an ad-hoc solver.
+                "penalty": classmethod(lambda cls, options: 0),
+                # Abstract instance methods.
+                "reset_problem": lambda self: placeholder(self),
+                "_import_problem": lambda self: placeholder(self),
+                "_update_problem": lambda self: placeholder(self),
+                "_solve": lambda self: placeholder(self),
+            },
+        )

         # Ad-hoc the dummy solver and prepare the problem for it.
         oldAdHocSolver = self.options.ad_hoc_solver
         extra_options["ad_hoc_solver"] = DummySolver
         problem = self.prepared(**extra_options)
@@ -1861,71 +1965,97 @@
                     elif options.solver:
                         solverName = get_solver(options.solver).names()[1]
                     else:
                         solverName = None

-                    print("Searching a solution strategy{}.".format(
-                    " for {}".format(solverName) if solverName else ""))
+                    print(
+                        "Searching a solution strategy{}.".format(
+                            " for {}".format(solverName) if solverName else ""
+                        )
+                    )

                 try:
                     self._strategy = Strategy.from_problem(
-                        self, **extra_options)
+                        self, **extra_options
+                    )
                 except NoStrategyFound as error:
                     raise SolutionFailure(1, error)

                 if verbose:
-                    print("Solution strategy:\n  {}".format(
-                        "\n  ".join(str(self._strategy).splitlines())))
+                    print(
+                        "Solution strategy:\n  {}".format(
+                            "\n  ".join(str(self._strategy).splitlines())
+                        )
+                    )
             else:
                 if verbose:
-                    print("Reusing strategy:\n  {}".format(
-                        "\n  ".join(str(self._strategy).splitlines())))
+                    print(
+                        "Reusing strategy:\n  {}".format(
+                            "\n  ".join(str(self._strategy).splitlines())
+                        )
+                    )

             # Execute the strategy to obtain one or more solutions.
             solutions = self._strategy.execute(**extra_options)

             # Report how many solutions were obtained, select the first.
             if isinstance(solutions, list):
                 assert all(isinstance(s, Solution) for s in solutions)

                 if not solutions:
                     raise SolutionFailure(
-                        2, "The solver returned an empty list of solutions.")
+                        2, "The solver returned an empty list of solutions."
+                    )

                 solution = solutions[0]

                 if verbose:
-                    print("Selecting the first of {} solutions obtained for "
-                        "processing.".format(len(solutions)))
+                    print(
+                        "Selecting the first of {} solutions obtained for "
+                        "processing.".format(len(solutions))
+                    )
             else:
                 assert isinstance(solutions, Solution)
                 solution = solutions

             # Report claimed solution state.
             if verbose:
-                print("Solver claims {} solution for {} problem.".format(
-                    solution.claimedStatus, solution.problemStatus))
+                print(
+                    "Solver claims {} solution for {} problem.".format(
+                        solution.claimedStatus, solution.problemStatus
+                    )
+                )

             # Validate the primal solution.
             if options.primals:
                 if solution.primalStatus != SS_OPTIMAL:
-                    raise SolutionFailure(3, "Primal solution state claimed {} "
+                    raise SolutionFailure(
+                        3,
+                        "Primal solution state claimed {} "
                         "but optimality is required (primals=True).".format(
-                        solution.primalStatus))
+                            solution.primalStatus
+                        ),
+                    )
                 elif None in solution.primals:
                     raise SolutionFailure(
-                        "The primal solution is incomplete (primals=True).")
+                        "The primal solution is incomplete (primals=True)."
+                    )

             # Validate the dual solution.
             if options.duals:
                 if solution.dualStatus != SS_OPTIMAL:
-                    raise SolutionFailure(4, "Dual solution state claimed {} "
+                    raise SolutionFailure(
+                        4,
+                        "Dual solution state claimed {} "
                         "but optimality is required (duals=True).".format(
-                        solution.dualStatus))
+                            solution.dualStatus
+                        ),
+                    )
                 elif None in solution.duals:
                     raise SolutionFailure(
-                        "The dual solution is incomplete (duals=True).")
+                        "The dual solution is incomplete (duals=True)."
+                    )

             if options.apply_solution:
                 if verbose:
                     print("Applying the solution.")

@@ -1947,17 +2077,23 @@
                 overhead = (solveTime - searchTime) / searchTime
             else:
                 overhead = float("inf")

             if verbose:
-                print("Search {:.1e}s, solve {:.1e}s, overhead {:.0%}."
-                    .format(searchTime, solveTime, overhead))
+                print(
+                    "Search {:.1e}s, solve {:.1e}s, overhead {:.0%}.".format(
+                        searchTime, solveTime, overhead
+                    )
+                )

         return solutions

-    @deprecated("2.0", reason="Misleading semantics. Maybe "
-        ":func:`picos.minimize` is what you want.")
+    @deprecated(
+        "2.0",
+        reason="Misleading semantics. Maybe "
+        ":func:`picos.minimize` is what you want.",
+    )
     def minimize(self, obj, **extra_options):
         """Look for a minimizing solution.

         Sets the objective to minimize the given objective function and calls
         the solver with the given additional options.
@@ -1975,12 +2111,15 @@
             override any existing objective function and direction.
         """
         self.objective = "min", obj
         return self.solve(**extra_options)

-    @deprecated("2.0", reason="Misleading semantics. Maybe "
-        ":func:`picos.maximize` is what you want.")
+    @deprecated(
+        "2.0",
+        reason="Misleading semantics. Maybe "
+        ":func:`picos.maximize` is what you want.",
+    )
     def maximize(self, obj, **extra_options):
         """Look for a maximization solution.

         Sets the objective to maximize the given objective function and calls
         the solver with the given additional options.
@@ -2034,30 +2173,36 @@
         :raises picos.uncertain.IntractableWorstCase:
             When computing the worst-case (expected) value of the constrained
             expression is not supported.
         """
         if inttol is not None:
-            throw_deprecation_warning("Variable integrality is now ensured on "
+            throw_deprecation_warning(
+                "Variable integrality is now ensured on "
                 "assignment of a value, so it does not need to be checked via "
-                "check_current_value_feasibility's old 'inttol' parameter.")
+                "check_current_value_feasibility's old 'inttol' parameter."
+            )

         if tol is None:
             tol = self._options.abs_prim_fsb_tol

         all_cons = list(self._constraints.values())
         all_cons += [
-            variable.bound_constraint for variable in self._variables.values()
-            if variable.bound_constraint]
+            variable.bound_constraint
+            for variable in self._variables.values()
+            if variable.bound_constraint
+        ]

         largest_violation = 0.0

         for constraint in all_cons:
             try:
                 slack = constraint.slack
             except IntractableWorstCase as error:
-                raise IntractableWorstCase("Failed to check worst-case or "
-                    "expected feasibility of {}: {}".format(constraint, error))
+                raise IntractableWorstCase(
+                    "Failed to check worst-case or "
+                    "expected feasibility of {}: {}".format(constraint, error)
+                )

             assert isinstance(slack, (float, cvx.matrix, cvx.spmatrix))
             if isinstance(slack, (float, cvx.spmatrix)):
                 slack = cvx.matrix(slack)  # Allow min, max.

@@ -2070,13 +2215,15 @@
             #        cone's slack can then have negative entries but still be
             #        feasible and declared infeasible here.
             # TODO: Add a "violation" interface to Constraint that replaces all
             #       the logic below.
             from ..expressions import Constant, PositiveSemidefiniteCone
-            if isinstance(constraint,
-                constraints.uncertain.ScenarioUncertainConicConstraint) \
-            and isinstance(constraint.cone, PositiveSemidefiniteCone):
+
+            if isinstance(
+                constraint,
+                constraints.uncertain.ScenarioUncertainConicConstraint,
+            ) and isinstance(constraint.cone, PositiveSemidefiniteCone):
                 hack = True
                 slack = Constant(slack).desvec.safe_value
             else:
                 hack = False

@@ -2099,12 +2246,14 @@

     # --------------------------------------------------------------------------
     # Legacy methods and properties.
     # --------------------------------------------------------------------------

-    _LEGACY_PROPERTY_REASON = "Still used internally by legacy code; will be " \
+    _LEGACY_PROPERTY_REASON = (
+        "Still used internally by legacy code; will be "
         "removed together with that code."
+    )

     @property
     @deprecated("2.0", reason=_LEGACY_PROPERTY_REASON)
     def countVar(self):
         """The same as :func:`len` applied to :attr:`variables`."""
@@ -2124,35 +2273,62 @@

     @property
     @deprecated("2.0", reason=_LEGACY_PROPERTY_REASON)
     def numberLSEConstraints(self):
         """Number of :class:`~picos.constraints.LogSumExpConstraint` stored."""
-        return len([c for c in self._constraints.values()
-            if isinstance(c, constraints.LogSumExpConstraint)])
+        return len(
+            [
+                c
+                for c in self._constraints.values()
+                if isinstance(c, constraints.LogSumExpConstraint)
+            ]
+        )

     @property
     @deprecated("2.0", reason=_LEGACY_PROPERTY_REASON)
     def numberSDPConstraints(self):
         """Number of :class:`~picos.constraints.LMIConstraint` stored."""
-        return len([c for c in self._constraints.values()
-            if isinstance(c, constraints.LMIConstraint)])
+        return len(
+            [
+                c
+                for c in self._constraints.values()
+                if isinstance(c, constraints.LMIConstraint)
+            ]
+        )

     @property
     @deprecated("2.0", reason=_LEGACY_PROPERTY_REASON)
     def numberQuadConstraints(self):
         """Number of quadratic constraints stored."""
-        return len([c for c in self._constraints.values() if isinstance(c, (
-            constraints.ConvexQuadraticConstraint,
-            constraints.ConicQuadraticConstraint,
-            constraints.NonconvexQuadraticConstraint))])
+        return len(
+            [
+                c
+                for c in self._constraints.values()
+                if isinstance(
+                    c,
+                    (
+                        constraints.ConvexQuadraticConstraint,
+                        constraints.ConicQuadraticConstraint,
+                        constraints.NonconvexQuadraticConstraint,
+                    ),
+                )
+            ]
+        )

     @property
     @deprecated("2.0", reason=_LEGACY_PROPERTY_REASON)
     def numberConeConstraints(self):
         """Number of quadratic conic constraints stored."""
-        return len([c for c in self._constraints.values() if isinstance(
-            c, (constraints.SOCConstraint, constraints.RSOCConstraint))])
+        return len(
+            [
+                c
+                for c in self._constraints.values()
+                if isinstance(
+                    c, (constraints.SOCConstraint, constraints.RSOCConstraint)
+                )
+            ]
+        )

     @deprecated("2.0", useInstead="value")
     def obj_value(self):
         """Objective function value.

@@ -2163,17 +2339,21 @@
             to denote an unvalued expression would raise
             :exc:`~picos.expressions.NotValued` instead.
         """
         if self._objective.feasibility:
             raise AttributeError(
-                "A feasibility problem has no objective value.")
+                "A feasibility problem has no objective value."
+            )

         value = self.value

         if self.value is None:
-            raise AttributeError("The objective {} is not fully valued."
-                .format(self._objective.function.string))
+            raise AttributeError(
+                "The objective {} is not fully valued.".format(
+                    self._objective.function.string
+                )
+            )
         else:
             return value

     @deprecated("2.0", useInstead="continuous")
     def is_continuous(self):
@@ -2213,14 +2393,18 @@
     @deprecated("2.0", useInstead="Problem.options")
     def verbosity(self):
         """Return the problem's current verbosity level."""
         return self._options.verbosity

-    @deprecated("2.0", reason="Variables can now be created independent of "
-        "problems, and do not need to be added to any problem explicitly.")
+    @deprecated(
+        "2.0",
+        reason="Variables can now be created independent of "
+        "problems, and do not need to be added to any problem explicitly.",
+    )
     def add_variable(
-            self, name, size=1, vtype='continuous', lower=None, upper=None):
+        self, name, size=1, vtype="continuous", lower=None, upper=None
+    ):
         r"""Legacy method to create a PICOS variable.

         :param str name: The name of the variable.

         :param size:
@@ -2280,18 +2464,23 @@
         elif vtype == "complex":
             return expressions.ComplexVariable(name, size)
         elif vtype == "hermitian":
             return expressions.HermitianVariable(name, size)
         elif vtype in ("semiint", "semicont"):
-            raise NotImplementedError("Variables with legacy types 'semiint' "
+            raise NotImplementedError(
+                "Variables with legacy types 'semiint' "
                 "and 'semicont' are not supported anymore as of PICOS 2.0. "
-                "If you need this functionality back, please open an issue.")
+                "If you need this functionality back, please open an issue."
+            )
         else:
             raise ValueError("Unknown legacy variable type '{}'.".format(vtype))

-    @deprecated("2.0", reason="Whether a problem references a variable is now"
-        " determined dynamically, so this method has no effect.")
+    @deprecated(
+        "2.0",
+        reason="Whether a problem references a variable is now"
+        " determined dynamically, so this method has no effect.",
+    )
     def remove_variable(self, name):
         """Does nothing."""
         pass

     @deprecated("2.0", useInstead="variables")
@@ -2311,11 +2500,12 @@
         """
         try:
             variable = self._variables[name]
         except KeyError:
             raise KeyError(
-                "The problem references no variable named '{}'.".format(name))
+                "The problem references no variable named '{}'.".format(name)
+            )
         else:
             variable.value = value

     @deprecated("2.0", useInstead="dual")
     def as_dual(self):
--- first pass
+++ second pass
@@ -2216,14 +2216,17 @@
             #        feasible and declared infeasible here.
             # TODO: Add a "violation" interface to Constraint that replaces all
             #       the logic below.
             from ..expressions import Constant, PositiveSemidefiniteCone

-            if isinstance(
-                constraint,
-                constraints.uncertain.ScenarioUncertainConicConstraint,
-            ) and isinstance(constraint.cone, PositiveSemidefiniteCone):
+            if (
+                isinstance(
+                    constraint,
+                    constraints.uncertain.ScenarioUncertainConicConstraint,
+                )
+                and isinstance(constraint.cone, PositiveSemidefiniteCone)
+            ):
                 hack = True
                 slack = Constant(slack).desvec.safe_value
             else:
                 hack = False
mvolfik commented 3 years ago

MCVE:

def f():
    def f():
        def f():
            from ..expressions import Constant, PositiveSemidefiniteCone
            if isinstance(constraint,
                constraints.uncertain.ScenarioUncertainConicConstraint) \
            and isinstance(constraint.cone, PositiveSemidefiniteCone):
                pass

Likely a duplicate of #1629 , as this file works after applying #1958

ichard26 commented 3 years ago

Hello!

All reproduction cases in this issue format without error on master. The fixing commit was 8672af35f052a636545e38110f0419ea92aeca0f from PR GH-2126. I'll be marking this issue as a duplicate of GH-1629 since that's what GH-2126 aimed to fix and it's highly likely this issue falls under GH-1629.

Since we use the issue tracker as a reflection of what's on master, I'll be closing this issue. If you have any issues, especially with the new (but stable) output, please open a new issue. Oh and the fix should be available in a published release soon, see GH-2125 for more info.

Thank you for reporting!