Repository: Qiskit/qiskit-optimization
Pull request #369 (2022-05-03 10:38:02)

Adds informative text formats to `repr` and `str` for problem related objects (part 2/3 refactor text representation of objects)
<!--
⚠️ If you do not respect this template, your pull request will be closed.
⚠️ Your pull request title should be short detailed and understandable for all.
⚠️ Also, please add it in the CHANGELOG file under Unreleased section.
⚠️ If your pull request fixes an open issue, please link to the issue.

✅ I have added the tests to cover my changes.
✅ I have updated the documentation accordingly.
✅ I have read the CONTRIBUTING document.
-->

### Summary

This adds informative text formats to `repr` and `str` for the following classes.
- LinearConstraint
- LinearExpression
- QuadraticConstraint
- QuadraticExpression
- QuadraticObjective
- Variable


### Details and comments

`repr` and `str` of `QuadraticProgram` and `OptimizationResult` will be updated in the next PR because they are used in tutorials and I have to update all tutorials. This PR does not affect existing tutorials.

The text of the formats is one line to be IDE friendly.

Vscode
![image](https://user-images.githubusercontent.com/31178928/166439130-52c3f4fb-4bf8-4139-b29d-db389e9731d2.png)

Pycharm
![image](https://user-images.githubusercontent.com/31178928/166439175-f75c5be1-9a5b-4640-b736-a6289ff97b42.png)

I added `__repr__` and `__str__` to comply with Python's convention.

> `object.__repr__(self)`
> Called by the repr() built-in function to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form <...some useful description...> should be returned.
https://docs.python.org/3/reference/datamodel.html#object.__repr__

> `object.__str__(self)`
> Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object. The return value must be a string object.
https://docs.python.org/3/reference/datamodel.html#object.__str__


coveralls: ## Pull Request Test Coverage Report for [Build 2263316241](https://coveralls.io/builds/48785840)

* **44** of **44**   **(100.0%)**  changed or added relevant lines in **6** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.1%**) to **91.35%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/48785840/badge)](https://coveralls.io/builds/48785840) |
| :-- | --: |
| Change from base [Build 2263161952](https://coveralls.io/builds/48785495): |  0.1% |
| Covered Lines: | 4256 |
| Relevant Lines: | 4659 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/.pylintdict b/.pylintdict
index fdbf66f..7829e9d 100644
--- a/.pylintdict
+++ b/.pylintdict
@@ -81,6 +81,7 @@ hamiltonian
 hamiltonians
 hastings
 hoyer
+ide
 imode
 init
 initializer
@@ -163,6 +164,7 @@ qubit
 qubits
 qubo
 readme
+repr
 representable
 rhobeg
 rhoend
diff --git a/qiskit_optimization/problems/linear_constraint.py b/qiskit_optimization/problems/linear_constraint.py
index 3672041..f7d0986 100644
--- a/qiskit_optimization/problems/linear_constraint.py
+++ b/qiskit_optimization/problems/linear_constraint.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2019, 2021.
+# (C) Copyright IBM 2019, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -79,3 +79,17 @@ class LinearConstraint(Constraint):
             The left-hand-side of the constraint given the variable values.
         """
         return self.linear.evaluate(x)
+
+    def __repr__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
+
+        lhs = expr2str(linear=self.linear, truncate=DEFAULT_TRUNCATE)
+        return f"<{self.__class__.__name__}: {lhs} {self.sense.label} {self.rhs} '{self.name}'>"
+
+    def __str__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str
+
+        lhs = expr2str(linear=self.linear)
+        return f"{lhs} {self.sense.label} {self.rhs} '{self.name}'"
diff --git a/qiskit_optimization/problems/linear_expression.py b/qiskit_optimization/problems/linear_expression.py
index cceea7b..663252c 100644
--- a/qiskit_optimization/problems/linear_expression.py
+++ b/qiskit_optimization/problems/linear_expression.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2019, 2021.
+# (C) Copyright IBM 2019, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -208,3 +208,15 @@ class LinearExpression(QuadraticProgramElement):
             l_b += min(lst)
             u_b += max(lst)
         return ExpressionBounds(lowerbound=l_b, upperbound=u_b)
+
+    def __repr__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
+
+        return f"<{self.__class__.__name__}: {expr2str(linear=self, truncate=DEFAULT_TRUNCATE)}>"
+
+    def __str__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str
+
+        return f"{expr2str(linear=self)}"
diff --git a/qiskit_optimization/problems/quadratic_constraint.py b/qiskit_optimization/problems/quadratic_constraint.py
index 636b8ae..629ecf6 100644
--- a/qiskit_optimization/problems/quadratic_constraint.py
+++ b/qiskit_optimization/problems/quadratic_constraint.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2019, 2021.
+# (C) Copyright IBM 2019, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -118,3 +118,17 @@ class QuadraticConstraint(Constraint):
             The left-hand-side of the constraint given the variable values.
         """
         return self.linear.evaluate(x) + self.quadratic.evaluate(x)
+
+    def __repr__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
+
+        lhs = expr2str(linear=self.linear, quadratic=self.quadratic, truncate=DEFAULT_TRUNCATE)
+        return f"<{self.__class__.__name__}: {lhs} {self.sense.label} {self.rhs} '{self.name}'>"
+
+    def __str__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str
+
+        lhs = expr2str(linear=self.linear, quadratic=self.quadratic)
+        return f"{lhs} {self.sense.label} {self.rhs} '{self.name}'"
diff --git a/qiskit_optimization/problems/quadratic_expression.py b/qiskit_optimization/problems/quadratic_expression.py
index d663b1d..952b761 100644
--- a/qiskit_optimization/problems/quadratic_expression.py
+++ b/qiskit_optimization/problems/quadratic_expression.py
@@ -283,3 +283,15 @@ class QuadraticExpression(QuadraticProgramElement):
             l_b += min(lst2)
             u_b += max(lst2)
         return ExpressionBounds(lowerbound=l_b, upperbound=u_b)
+
+    def __repr__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
+
+        return f"<{self.__class__.__name__}: {expr2str(quadratic=self, truncate=DEFAULT_TRUNCATE)}>"
+
+    def __str__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str
+
+        return f"{expr2str(quadratic=self)}"
diff --git a/qiskit_optimization/problems/quadratic_objective.py b/qiskit_optimization/problems/quadratic_objective.py
index 6e5ab6f..6fee70a 100644
--- a/qiskit_optimization/problems/quadratic_objective.py
+++ b/qiskit_optimization/problems/quadratic_objective.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2019, 2021.
+# (C) Copyright IBM 2019, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -200,3 +200,17 @@ class QuadraticObjective(QuadraticProgramElement):
                 "Need to define the objective function after defining all variables"
             )
         return self.linear.evaluate_gradient(x) + self.quadratic.evaluate_gradient(x)
+
+    def __repr__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str, DEFAULT_TRUNCATE
+
+        expr_str = expr2str(self.constant, self.linear, self.quadratic, DEFAULT_TRUNCATE)
+        return f"<{self.__class__.__name__}: {self._sense.name.lower()} {expr_str}>"
+
+    def __str__(self):
+        # pylint: disable=cyclic-import
+        from ..translators.prettyprint import expr2str
+
+        expr_str = expr2str(self.constant, self.linear, self.quadratic)
+        return f"{self._sense.name.lower()} {expr_str}"
diff --git a/qiskit_optimization/problems/variable.py b/qiskit_optimization/problems/variable.py
index f7d351a..ecbf880 100644
--- a/qiskit_optimization/problems/variable.py
+++ b/qiskit_optimization/problems/variable.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2019, 2021.
+# (C) Copyright IBM 2019, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -148,3 +148,13 @@ class Variable(QuadraticProgramElement):
             variable type.
         """
         return self.name, self.lowerbound, self.upperbound, self.vartype
+
+    def __repr__(self):
+        return f"<{self.__class__.__name__}: {str(self)}>"
+
+    def __str__(self):
+        if self._vartype == VarType.BINARY:
+            return f"{self.name} ({self._vartype.name.lower()})"
+        lowerbound = "" if self._lowerbound == -INFINITY else f"{self._lowerbound} <= "
+        upperbound = "" if self._upperbound == INFINITY else f" <= {self._upperbound}"
+        return f"{lowerbound}{self.name}{upperbound} ({self._vartype.name.lower()})"
diff --git a/qiskit_optimization/translators/prettyprint.py b/qiskit_optimization/translators/prettyprint.py
index 96b7ee4..a14a04d 100644
--- a/qiskit_optimization/translators/prettyprint.py
+++ b/qiskit_optimization/translators/prettyprint.py
@@ -98,7 +98,7 @@ def _check_name(name: str, name_type: str) -> None:
         raise QiskitOptimizationError(f"{name_type} name is not printable: {repr(name)}")
 
 
-def _expr2str(
+def expr2str(
     constant: float = 0.0,
     linear: Optional[LinearExpression] = None,
     quadratic: Optional[QuadraticExpression] = None,
@@ -180,7 +180,7 @@ def prettyprint(quadratic_program: QuadraticProgram) -> str:
             buf.write("Maximize\n")
         buf.write("  ")
         buf.write(
-            _expr2str(
+            expr2str(
                 quadratic_program.objective.constant,
                 quadratic_program.objective.linear,
                 quadratic_program.objective.quadratic,
@@ -196,7 +196,7 @@ def prettyprint(quadratic_program: QuadraticProgram) -> str:
             for cst in quadratic_program.linear_constraints:
                 _check_name(cst.name, "Linear constraint")
                 buf.write(
-                    f"    {_expr2str(linear=cst.linear)}"
+                    f"    {expr2str(linear=cst.linear)}"
                     f" {cst.sense.label} {_int_if_close(cst.rhs)}"
                     f"  '{cst.name}'\n"
                 )
@@ -205,7 +205,7 @@ def prettyprint(quadratic_program: QuadraticProgram) -> str:
             for cst2 in quadratic_program.quadratic_constraints:
                 _check_name(cst2.name, "Quadratic constraint")
                 buf.write(
-                    f"    {_expr2str(linear=cst2.linear, quadratic=cst2.quadratic)}"
+                    f"    {expr2str(linear=cst2.linear, quadratic=cst2.quadratic)}"
                     f" {cst2.sense.label} {_int_if_close(cst2.rhs)}"
                     f"  '{cst2.name}'\n"
                 )
diff --git a/releasenotes/notes/add-repr-str-9a33af0a6ffdca6e.yaml b/releasenotes/notes/add-repr-str-9a33af0a6ffdca6e.yaml
new file mode 100644
index 0000000..4a47fca
--- /dev/null
+++ b/releasenotes/notes/add-repr-str-9a33af0a6ffdca6e.yaml
@@ -0,0 +1,13 @@
+---
+features:
+  - |
+    Adds informative text formats to ``str`` and ``repr`` functions of the following objects.
+    The formats are IDE friendly, i.e., the text is one line.
+
+    - :class:`~qiskit_optimization.problems.LinearConstraint`
+    - :class:`~qiskit_optimization.problems.LinearExpression`
+    - :class:`~qiskit_optimization.problems.QuadraticConstraint`
+    - :class:`~qiskit_optimization.problems.QuadraticExpression`
+    - :class:`~qiskit_optimization.problems.QuadraticObjective`
+    - :class:`~qiskit_optimization.problems.Variable`
+


Test patch:
diff --git a/test/problems/test_linear_constraint.py b/test/problems/test_linear_constraint.py
index 07eb152..79e4731 100644
--- a/test/problems/test_linear_constraint.py
+++ b/test/problems/test_linear_constraint.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -24,6 +24,23 @@ from qiskit_optimization.problems import Constraint
 class TestLinearConstraint(QiskitOptimizationTestCase):
     """Test LinearConstraint."""
 
+    def setUp(self) -> None:
+        super().setUp()
+        self.quadratic_program = QuadraticProgram()
+        self.quadratic_program.binary_var_list(3, name="x")
+        self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "<=", 1)
+        self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "<", 1)
+        self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "LE", 1)
+        self.quadratic_program.linear_constraint({"x0": 1, "x1": -2}, "L", 1)
+        self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "==", 2)
+        self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "=", 2)
+        self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "EQ", 2)
+        self.quadratic_program.linear_constraint({"x0": -1, "x1": 2}, "E", 2)
+        self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, ">=", 3)
+        self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, ">", 3)
+        self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, "GE", 3)
+        self.quadratic_program.linear_constraint({"x1": 2, "x2": -1}, "G", 3)
+
     def test_init(self):
         """test init."""
 
@@ -142,6 +159,41 @@ class TestLinearConstraint(QiskitOptimizationTestCase):
             quadratic_program.get_linear_constraint(5),
         )
 
+    def test_str(self):
+        """Test str"""
+        self.assertEqual(self.quadratic_program.get_num_linear_constraints(), 12)
+        for i in range(0, 4):
+            self.assertEqual(
+                str(self.quadratic_program.get_linear_constraint(i)), f"x0 - 2*x1 <= 1 'c{i}'"
+            )
+        for i in range(4, 8):
+            self.assertEqual(
+                str(self.quadratic_program.get_linear_constraint(i)), f"-x0 + 2*x1 == 2 'c{i}'"
+            )
+        for i in range(8, 12):
+            self.assertEqual(
+                str(self.quadratic_program.get_linear_constraint(i)), f"2*x1 - x2 >= 3 'c{i}'"
+            )
+
+    def test_repr(self):
+        """Test repr"""
+        self.assertEqual(self.quadratic_program.get_num_linear_constraints(), 12)
+        for i in range(0, 4):
+            self.assertEqual(
+                repr(self.quadratic_program.get_linear_constraint(i)),
+                f"<LinearConstraint: x0 - 2*x1 <= 1 'c{i}'>",
+            )
+        for i in range(4, 8):
+            self.assertEqual(
+                repr(self.quadratic_program.get_linear_constraint(i)),
+                f"<LinearConstraint: -x0 + 2*x1 == 2 'c{i}'>",
+            )
+        for i in range(8, 12):
+            self.assertEqual(
+                repr(self.quadratic_program.get_linear_constraint(i)),
+                f"<LinearConstraint: 2*x1 - x2 >= 3 'c{i}'>",
+            )
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/problems/test_linear_expression.py b/test/problems/test_linear_expression.py
index 4f029da..03e07c9 100644
--- a/test/problems/test_linear_expression.py
+++ b/test/problems/test_linear_expression.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -173,6 +173,31 @@ class TestLinearExpression(QiskitOptimizationTestCase):
             coefficients_list = list(range(3))
             _ = LinearExpression(quadratic_program, coefficients_list).bounds
 
+    def test_str_repr(self):
+        """Test str and repr"""
+        with self.subTest("5 variables"):
+            n = 5
+            quadratic_program = QuadraticProgram()
+            quadratic_program.binary_var_list(n)  # x0,...,x4
+            expr = LinearExpression(quadratic_program, [float(e) for e in range(n)])
+            self.assertEqual(str(expr), "x1 + 2*x2 + 3*x3 + 4*x4")
+            self.assertEqual(repr(expr), "<LinearExpression: x1 + 2*x2 + 3*x3 + 4*x4>")
+
+        with self.subTest("50 variables"):
+            # pylint: disable=cyclic-import
+            from qiskit_optimization.translators.prettyprint import DEFAULT_TRUNCATE
+
+            n = 50
+            quadratic_program = QuadraticProgram()
+            quadratic_program.binary_var_list(n)  # x0,...,x49
+            expr = LinearExpression(quadratic_program, [float(e) for e in range(n)])
+
+            expected = " ".join(
+                ["x1"] + sorted([f"+ {i}*x{i}" for i in range(2, n)], key=lambda e: e.split(" ")[1])
+            )
+            self.assertEqual(str(expr), expected)
+            self.assertEqual(repr(expr), f"<LinearExpression: {expected[:DEFAULT_TRUNCATE]}...>")
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/problems/test_quadratic_constraint.py b/test/problems/test_quadratic_constraint.py
index 6a78312..d094e50 100644
--- a/test/problems/test_quadratic_constraint.py
+++ b/test/problems/test_quadratic_constraint.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -24,6 +24,47 @@ from qiskit_optimization.problems import Constraint
 class TestQuadraticConstraint(QiskitOptimizationTestCase):
     """Test QuadraticConstraint."""
 
+    def setUp(self) -> None:
+        super().setUp()
+        self.quadratic_program = QuadraticProgram()
+        self.quadratic_program.binary_var_list(3, name="x")
+        self.quadratic_program.quadratic_constraint(
+            {"x0": 1, "x1": -2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "<=", 1
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": 1, "x1": -2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "<", 1
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": 1, "x1": -2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "LE", 1
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": 1, "x1": -2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "L", 1
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": -1, "x1": 2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "==", 2
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": -1, "x1": 2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "=", 2
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": -1, "x1": 2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "EQ", 2
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x0": -1, "x1": 2}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "E", 2
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x1": 2, "x2": -1}, {("x0", "x0"): -1, ("x2", "x1"): 2}, ">=", 3
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x1": 2, "x2": -1}, {("x0", "x0"): -1, ("x2", "x1"): 2}, ">", 3
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x1": 2, "x2": -1}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "GE", 3
+        )
+        self.quadratic_program.quadratic_constraint(
+            {"x1": 2, "x2": -1}, {("x0", "x0"): -1, ("x2", "x1"): 2}, "G", 3
+        )
+
     def test_init(self):
         """test init."""
 
@@ -176,6 +217,44 @@ class TestQuadraticConstraint(QiskitOptimizationTestCase):
 
         self.assertEqual(quadratic_program.quadratic_constraints[5].evaluate(linear_coeffs), 930.0)
 
+    def test_str(self):
+        """Test str"""
+        self.assertEqual(self.quadratic_program.get_num_quadratic_constraints(), 12)
+        for i in range(0, 4):
+            self.assertEqual(
+                str(self.quadratic_program.get_quadratic_constraint(i)),
+                f"-x0^2 + 2*x1*x2 + x0 - 2*x1 <= 1 'q{i}'",
+            )
+        for i in range(4, 8):
+            self.assertEqual(
+                str(self.quadratic_program.get_quadratic_constraint(i)),
+                f"-x0^2 + 2*x1*x2 - x0 + 2*x1 == 2 'q{i}'",
+            )
+        for i in range(8, 12):
+            self.assertEqual(
+                str(self.quadratic_program.get_quadratic_constraint(i)),
+                f"-x0^2 + 2*x1*x2 + 2*x1 - x2 >= 3 'q{i}'",
+            )
+
+    def test_repr(self):
+        """Test repr"""
+        self.assertEqual(self.quadratic_program.get_num_quadratic_constraints(), 12)
+        for i in range(0, 4):
+            self.assertEqual(
+                repr(self.quadratic_program.get_quadratic_constraint(i)),
+                f"<QuadraticConstraint: -x0^2 + 2*x1*x2 + x0 - 2*x1 <= 1 'q{i}'>",
+            )
+        for i in range(4, 8):
+            self.assertEqual(
+                repr(self.quadratic_program.get_quadratic_constraint(i)),
+                f"<QuadraticConstraint: -x0^2 + 2*x1*x2 - x0 + 2*x1 == 2 'q{i}'>",
+            )
+        for i in range(8, 12):
+            self.assertEqual(
+                repr(self.quadratic_program.get_quadratic_constraint(i)),
+                f"<QuadraticConstraint: -x0^2 + 2*x1*x2 + 2*x1 - x2 >= 3 'q{i}'>",
+            )
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/problems/test_quadratic_expression.py b/test/problems/test_quadratic_expression.py
index 3e9f3f3..0b5a63b 100644
--- a/test/problems/test_quadratic_expression.py
+++ b/test/problems/test_quadratic_expression.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -232,6 +232,47 @@ class TestQuadraticExpression(QiskitOptimizationTestCase):
             q_p.continuous_var(-1, INFINITY, "z")
             _ = QuadraticExpression(q_p, {("x", "y"): 1, ("y", "z"): 2, ("z", "z"): 3}).bounds
 
+    def test_str_repr(self):
+        """Test str and repr"""
+        with self.subTest("5 variables"):
+            n = 5
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(n)  # x0,...,x4
+            expr = QuadraticExpression(q_p, {(i, i): float(i) for i in range(n)})
+            self.assertEqual(str(expr), "x1^2 + 2*x2^2 + 3*x3^2 + 4*x4^2")
+            self.assertEqual(repr(expr), "<QuadraticExpression: x1^2 + 2*x2^2 + 3*x3^2 + 4*x4^2>")
+
+            expr = QuadraticExpression(q_p, {(i, (i + 1) % n): float(i) for i in range(n)})
+            self.assertEqual(str(expr), "4*x0*x4 + x1*x2 + 2*x2*x3 + 3*x3*x4")
+            self.assertEqual(
+                repr(expr), "<QuadraticExpression: 4*x0*x4 + x1*x2 + 2*x2*x3 + 3*x3*x4>"
+            )
+
+        with self.subTest("50 variables"):
+            # pylint: disable=cyclic-import
+            from qiskit_optimization.translators.prettyprint import DEFAULT_TRUNCATE
+
+            n = 50
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(n)  # x0,...,x49
+            expr = QuadraticExpression(q_p, {(i, i): float(i) for i in range(n)})
+            expected = " ".join(
+                ["x1^2"]
+                + sorted([f"+ {i}*x{i}^2" for i in range(2, n)], key=lambda e: e.split(" ")[1])
+            )
+            self.assertEqual(str(expr), expected)
+            self.assertEqual(repr(expr), f"<QuadraticExpression: {expected[:DEFAULT_TRUNCATE]}...>")
+
+            expr = QuadraticExpression(q_p, {(i, (i + 1) % n): float(i) for i in range(n)})
+            expected = " ".join(
+                [f"{n-1}*x0*x{n-1}", "+ x1*x2"]
+                + sorted(
+                    [f"+ {i}*x{i}*x{i + 1}" for i in range(2, n - 1)], key=lambda e: e.split(" ")[1]
+                )
+            )
+            self.assertEqual(str(expr), expected)
+            self.assertEqual(repr(expr), f"<QuadraticExpression: {expected[:DEFAULT_TRUNCATE]}...>")
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/problems/test_quadratic_objective.py b/test/problems/test_quadratic_objective.py
index 86da07e..050e3df 100644
--- a/test/problems/test_quadratic_objective.py
+++ b/test/problems/test_quadratic_objective.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -101,6 +101,127 @@ class TestQuadraticObjective(QiskitOptimizationTestCase):
         quadratic_program.objective.sense = quadratic_program.objective.Sense.MINIMIZE
         self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE)
 
+    def test_str_repr(self):
+        """Test str and repr"""
+        with self.subTest("4 variables"):
+            n = 4
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(n)  # x0,...,x3
+            quad = {(i, i): float(i) for i in range(n)}
+            lin = [float(i) for i in range(n)]
+
+            q_p.maximize(quadratic=quad, linear=lin, constant=n)
+            expected = "maximize x1^2 + 2*x2^2 + 3*x3^2 + x1 + 2*x2 + 3*x3 + 4"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.minimize(quadratic=quad, linear=lin, constant=n)
+            expected = "minimize x1^2 + 2*x2^2 + 3*x3^2 + x1 + 2*x2 + 3*x3 + 4"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            quad = {(i, (i + 1) % n): i for i in range(n)}
+
+            q_p.maximize(quadratic=quad, linear=lin, constant=0)
+            expected = "maximize 3*x0*x3 + x1*x2 + 2*x2*x3 + x1 + 2*x2 + 3*x3"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.minimize(quadratic=quad, linear=lin, constant=0)
+            expected = "minimize 3*x0*x3 + x1*x2 + 2*x2*x3 + x1 + 2*x2 + 3*x3"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+        with self.subTest("50 variables"):
+            # pylint: disable=cyclic-import
+            from qiskit_optimization.translators.prettyprint import DEFAULT_TRUNCATE
+
+            n = 50
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(n)
+            quad = {(i, i): float(i) for i in range(n)}
+            lin = [float(i) for i in range(n)]
+
+            expected = " ".join(
+                ["x1^2"]
+                + sorted([f"+ {i}*x{i}^2" for i in range(2, n)], key=lambda e: e.split(" ")[1])
+                + ["+ x1"]
+                + sorted([f"+ {i}*x{i}" for i in range(2, n)], key=lambda e: e.split(" ")[1])
+                + [f"+ {n}"]
+            )
+            q_p.maximize(quadratic=quad, linear=lin, constant=n)
+            self.assertEqual(str(q_p.objective), f"maximize {expected}")
+            self.assertEqual(
+                repr(q_p.objective),
+                f"<QuadraticObjective: maximize {expected[:DEFAULT_TRUNCATE]}...>",
+            )
+
+            q_p.minimize(quadratic=quad, linear=lin, constant=n)
+            self.assertEqual(str(q_p.objective), f"minimize {expected}")
+            self.assertEqual(
+                repr(q_p.objective),
+                f"<QuadraticObjective: minimize {expected[:DEFAULT_TRUNCATE]}...>",
+            )
+
+            quad = {(i + 1, i): float(i + 1) for i in range(2)}
+            lin = [float(i) for i in range(n)]
+            expected = " ".join(
+                ["x0*x1 + 2*x1*x2"]
+                + ["+ x1"]
+                + sorted([f"+ {i}*x{i}" for i in range(2, n)], key=lambda e: e.split(" ")[1])
+                + [f"+ {n}"]
+            )
+            q_p.maximize(quadratic=quad, linear=lin, constant=n)
+            self.assertEqual(str(q_p.objective), f"maximize {expected}")
+            self.assertEqual(
+                repr(q_p.objective),
+                f"<QuadraticObjective: maximize {expected[:DEFAULT_TRUNCATE]}...>",
+            )
+
+            q_p.minimize(quadratic=quad, linear=lin, constant=n)
+            self.assertEqual(str(q_p.objective), f"minimize {expected}")
+            self.assertEqual(
+                repr(q_p.objective),
+                f"<QuadraticObjective: minimize {expected[:DEFAULT_TRUNCATE]}...>",
+            )
+
+        with self.subTest("only constant"):
+            q_p = QuadraticProgram()
+
+            q_p.maximize()
+            expected = "maximize 0"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.minimize()
+            expected = "minimize 0"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.maximize(constant=-1.23)
+            expected = "maximize -1.23"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.minimize(constant=-1.23)
+            expected = "minimize -1.23"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+        with self.subTest("1 variable"):
+            q_p = QuadraticProgram()
+            q_p.binary_var("z")
+
+            q_p.maximize(linear=[1], quadratic={("z", "z"): -1}, constant=1)
+            expected = "maximize -z^2 + z + 1"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
+            q_p.minimize(linear=[1], quadratic={("z", "z"): -1}, constant=1)
+            expected = "minimize -z^2 + z + 1"
+            self.assertEqual(str(q_p.objective), expected)
+            self.assertEqual(repr(q_p.objective), f"<QuadraticObjective: {expected}>")
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/problems/test_variable.py b/test/problems/test_variable.py
index 600b5bc..b81c946 100644
--- a/test/problems/test_variable.py
+++ b/test/problems/test_variable.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2020, 2021.
+# (C) Copyright IBM 2020, 2022.
 #
 # This code is licensed under the Apache License, Version 2.0. You may
 # obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -51,6 +51,50 @@ class TestVariable(QiskitOptimizationTestCase):
         self.assertEqual(variable.upperbound, INFINITY)
         self.assertEqual(variable.vartype, Variable.Type.CONTINUOUS)
 
+    def test_str_repr(self):
+        """test str and repr"""
+        quadratic_program = QuadraticProgram()
+
+        with self.subTest("binary"):
+            bin_var = quadratic_program.binary_var(name="x")
+            expected = "x (binary)"
+            self.assertEqual(str(bin_var), expected)
+            self.assertEqual(repr(bin_var), f"<Variable: {expected}>")
+
+        with self.subTest("integer 1"):
+            int_var = quadratic_program.integer_var(name="y1")
+            expected = "0 <= y1 (integer)"
+            self.assertEqual(str(int_var), expected)
+            self.assertEqual(repr(int_var), f"<Variable: {expected}>")
+
+        with self.subTest("integer 2"):
+            int_var2 = quadratic_program.integer_var(lowerbound=5, upperbound=10, name="y2")
+            expected = "5 <= y2 <= 10 (integer)"
+            self.assertEqual(str(int_var2), expected)
+            self.assertEqual(repr(int_var2), f"<Variable: {expected}>")
+
+        with self.subTest("continuous 1"):
+            con_var = quadratic_program.continuous_var(name="z1")
+            expected = "0 <= z1 (continuous)"
+            self.assertEqual(str(con_var), expected)
+            self.assertEqual(repr(con_var), f"<Variable: {expected}>")
+
+        with self.subTest("continuous 2"):
+            con_var2 = quadratic_program.continuous_var(
+                lowerbound=-10.0, upperbound=100.0, name="z2"
+            )
+            expected = "-10.0 <= z2 <= 100.0 (continuous)"
+            self.assertEqual(str(con_var2), expected)
+            self.assertEqual(repr(con_var2), f"<Variable: {expected}>")
+
+        with self.subTest("continuous 3"):
+            con_var3 = quadratic_program.continuous_var(
+                lowerbound=-INFINITY, upperbound=100.0, name="z3"
+            )
+            expected = "z3 <= 100.0 (continuous)"
+            self.assertEqual(str(con_var3), expected)
+            self.assertEqual(repr(con_var3), f"<Variable: {expected}>")
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/translators/test_prettyprint.py b/test/translators/test_prettyprint.py
index 5685bc0..43e06b8 100644
--- a/test/translators/test_prettyprint.py
+++ b/test/translators/test_prettyprint.py
@@ -16,7 +16,7 @@ from test.optimization_test_case import QiskitOptimizationTestCase
 
 from qiskit_optimization.exceptions import QiskitOptimizationError
 from qiskit_optimization.problems import QuadraticProgram
-from qiskit_optimization.translators.prettyprint import prettyprint
+from qiskit_optimization.translators.prettyprint import expr2str, prettyprint
 
 
 class TestPrettyprint(QiskitOptimizationTestCase):
@@ -304,3 +304,64 @@ class TestPrettyprint(QiskitOptimizationTestCase):
             q_p = QuadraticProgram()
             q_p.quadratic_constraint(name=name)
             _ = q_p.prettyprint()
+
+    def test_expr2str(self):
+        """test expr2str"""
+
+        with self.subTest("constant"):
+            self.assertEqual(expr2str(), "0")
+            self.assertEqual(expr2str(12345), "12345")
+            self.assertEqual(expr2str(1e3), "1000")
+            self.assertEqual(expr2str(1e20), "1e+20")
+            self.assertEqual(expr2str(1e-5), "1e-05")
+            self.assertEqual(expr2str(1e-9), "1e-09")
+            self.assertEqual(expr2str(1e-10), "0")
+
+        with self.subTest("linear"):
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(3)
+            q_p.linear_constraint({0: 1, 1: 2, 2: 3}, "==", 0)
+            self.assertEqual(
+                expr2str(linear=q_p.get_linear_constraint(0).linear), "x0 + 2*x1 + 3*x2"
+            )
+            self.assertEqual(
+                expr2str(constant=-1, linear=q_p.get_linear_constraint(0).linear),
+                "x0 + 2*x1 + 3*x2 - 1",
+            )
+
+        with self.subTest("quadratic"):
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(3)
+            q_p.quadratic_constraint(
+                quadratic={(0, 0): 1, (1, 2): 2, (2, 0): -3}, sense="==", rhs=0
+            )
+            self.assertEqual(
+                expr2str(quadratic=q_p.get_quadratic_constraint(0).quadratic),
+                "x0^2 - 3*x0*x2 + 2*x1*x2",
+            )
+            self.assertEqual(
+                expr2str(constant=1, quadratic=q_p.get_quadratic_constraint(0).quadratic),
+                "x0^2 - 3*x0*x2 + 2*x1*x2 + 1",
+            )
+
+        with self.subTest("truncate"):
+            self.assertEqual(expr2str(123456789, truncate=5), "12345...")
+
+            q_p = QuadraticProgram()
+            q_p.binary_var_list(3, name="long_name")
+            q_p.linear_constraint({0: 1, 1: 2, 2: 3}, "==", 0)
+            self.assertEqual(
+                expr2str(linear=q_p.get_linear_constraint(0).linear, truncate=10), "long_name0..."
+            )
+            self.assertEqual(
+                expr2str(linear=q_p.get_linear_constraint(0).linear, truncate=20),
+                "long_name0 + 2*long_...",
+            )
+
+            q_p.quadratic_constraint(
+                quadratic={(0, 0): 1, (1, 2): 2, (2, 0): -3}, sense="==", rhs=0
+            )
+            self.assertEqual(
+                expr2str(quadratic=q_p.get_quadratic_constraint(0).quadratic, truncate=20),
+                "long_name0^2 - 3*lon...",
+            )