Repository: Qiskit/qiskit-optimization
Pull request #366 (2022-04-25 08:41:37)

Add `prettyprint` (part 1/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 is part 1 of #184.

This PR introduces `prettyprint` function and `QuadraticProgram.prettyprint` method to generate a pretty-printed string of a `QuadraticProgram` object.


### Details and comments

We use Docplex's `export_as_lp_string` as a kind of pretty print of QP now.
But, it's good to have our own pretty printing format that is more comprehensive than Docplex's.
Moreover, it makes us easier to deal with pretty printing of planned new features such as higher order polynomials and semi-definite programming (Docplex cannot handle them).

The format is based on the discussion https://github.com/Qiskit/qiskit-optimization/pull/184#issuecomment-899025806
~~A minor modification is that I omitted a symbol `*` of the multiplication of two variables, e.g., `x * y` (before) -> `x y` (current) because the discussion did not have `*` of the multiplication of a scalar coefficient and a variable, e.g., `2 x` (before and current). So, I make them consistent. The output of the multiplication of a scalar and two variables would be `2 x * y` (before) -> `2 x y` (current)~~

I updated the multiplication to use only `*` between scalars and variables.

Variable names with " ", "+", "-", and "*" are translated into `("{name}")` to avoid confusion. These symbols are used to represent expressions.

Minor change:
- Updated `QuadraticProgram` to show a warning message if non-printable strings are set as problem name, variable name, or constraint name.
  - `prettyprint` raises an error if non-printable string is given, because it cannot be displayed.
- Updated `QuadraticProgram.{binary_var, integer_var, continuous_var}` to use the default name if an empty name is given.

Example 1: usual use case
```python
from qiskit_optimization.problems import QuadraticProgram
from qiskit_optimization.translators.prettyprint import prettyprint

qp = QuadraticProgram('problem 1')
qp.integer_var(-1, 2, 'x')
qp.integer_var(-1, 2, 'y')
qp.continuous_var(-1, name='z')
qp.binary_var('u')
qp.binary_var('v')
qp.binary_var_list(10)
qp.integer_var_list(3,name='φ')
qp.continuous_var_list(3,name='η')
qp.minimize(constant=3, linear={'x': 2, 'y': 3}, quadratic={('u', 'x'): -1})
qp.linear_constraint({'x': 1, 'y': -2}, '>=', 2, name='lin_GE')
qp.linear_constraint({'x': 2, 'y': -1}, '==', 1, name='lin_EQ')
qp.quadratic_constraint({'x': 1, 'u': 1}, {(3, 4): 1, (5, 6): -1}, '<=', 1, name='quad_LE')
qp.quadratic_constraint({'x': 2, 'y': -1}, {('z', 'z'): -1}, '<=', 1)

# the following two lines return the same output
#print(prettyprint(qp))
print(qp.prettyprint())
```

output
```
Problem name: problem 1

Minimize
  -x*u + 2*x + 3*y + 3

Subject to
  Linear constraints (2)
    x - 2*y >= 2  'lin_GE'
    2*x - y == 1  'lin_EQ'

  Quadratic constraints (2)
    u*v - x5*x6 + u + x <= 1  'quad_LE'
    -z^2 + 2*x - y <= 1  'q1'

  Integer variables (5)
    -1 <= x <= 2
    -1 <= y <= 2
    0 <= φ15
    0 <= φ16
    0 <= φ17

  Continuous variables (4)
    -1 <= z
    0 <= η18
    0 <= η19
    0 <= η20

  Binary variables (12)
    u v x5 x6 x7 x8 x9 x10 x11 x12 x13 x14
```

------------------
Example 2: corner case of variable names with symbols
The following does not take a special care of `+u` or `-v`; it is a similar way of docplex's prettyprint and gurobipy's display. https://github.com/Qiskit/qiskit-optimization/pull/366#issuecomment-1111800754
```python
from qiskit_optimization.problems import QuadraticProgram

qp = QuadraticProgram('problem 1')
qp.integer_var(-1, 2, 'x')
qp.integer_var(-1, 2, 'y')
qp.continuous_var(-1, name=' z')
qp.binary_var('+u')
qp.binary_var('-v')
qp.binary_var('*w')
qp.binary_var_list(10)
qp.integer_var_list(3, name='φ')
qp.continuous_var_list(3, name='η')
qp.minimize(constant=3, linear={'x': 2, 'y': 3}, quadratic={('+u', 'x'): -1})
qp.linear_constraint({'x': 1, 'y': -2}, '>=', 2, name='lin_GE')
qp.linear_constraint({'x': 2, 'y': -1}, '==', 1, name='lin_EQ')
qp.quadratic_constraint({'x': 1, '+u': 1}, {(3, 4): 1, (5, 6): -1}, '<=', 1, name='quad_LE')
qp.quadratic_constraint({'x': 2, 'y': -1}, {(' z', ' z'): -1}, '<=', 1)

print(qp.prettyprint())
```

output
```
Problem name: problem 1

Minimize
  -x*+u + 2*x + 3*y + 3

Subject to
  Linear constraints (2)
    x - 2*y >= 2  'lin_GE'
    2*x - y == 1  'lin_EQ'

  Quadratic constraints (2)
    -*w*x6 + +u*-v + +u + x <= 1  'quad_LE'
    - z^2 + 2*x - y <= 1  'q1'

  Integer variables (5)
    -1 <= x <= 2
    -1 <= y <= 2
    0 <= φ16
    0 <= φ17
    0 <= φ18

  Continuous variables (4)
    -1 <=  z
    0 <= η19
    0 <= η20
    0 <= η21

  Binary variables (13)
    +u -v *w x6 x7 x8 x9 x10 x11 x12 x13 x14 x15
```



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

* **124** of **129**   **(96.12%)**  changed or added relevant lines in **4** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.1%**) to **91.137%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [qiskit_optimization/problems/quadratic_program.py](https://coveralls.io/builds/48528930/source?filename=qiskit_optimization%2Fproblems%2Fquadratic_program.py#L1084) | 1 | 3 | 33.33%
| [qiskit_optimization/translators/prettyprint.py](https://coveralls.io/builds/48528930/source?filename=qiskit_optimization%2Ftranslators%2Fprettyprint.py#L39) | 114 | 117 | 97.44%
<!-- | **Total:** | **124** | **129** | **96.12%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/48528930/badge)](https://coveralls.io/builds/48528930) |
| :-- | --: |
| Change from base [Build 2201697704](https://coveralls.io/builds/48452404): |  0.1% |
| Covered Lines: | 4185 |
| Relevant Lines: | 4592 |

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

woodsp-ibm: I must admit I find having spaces there, instead of *, makes it that I need to look more closely at things. Its not so easy to see at a glance. The spaces can be interpreted either as separation or multiply and that duality, for me, makes the one with spaces take more effort to look at (versus one with explicit *) as per below
```
u v - x5 x6 + u + x <= 1

u*v - x5*x6 + u + x <= 1
```
I see that docplex leaves out the multiply, as you say, on constants, but why not have it, and then have consistency with it present in all places.
```
x - 2 y >= 2

x - 2*y >= 2
```

I think removing the space, or multiply, and simply putting the variables together
```
uv  - x5x6 + u + x <= 1

x - 2y >= 2
```
while that might work it could become ambiguous depending on the user choice of variables should I also have a variable 'uv' or a variable '2y' (I don't think there are any naming restrictions on doing that right). So that seems like a non-starter.
t-imamichi: Thank you for the feedback on the appearance. I want a format that is easy to see at a glance.
I think we need at least one character to separate variables because variable name "uv" might be possible. 
I'm fine to use `"*"` for multiplication. What do you think of the following examples?
```
option 1: 2 u v - 3 x5 x6 + 4 u - x <= 1 (current)

option 2: 2*u*v - 3*x5*x6 + 4*u - x <= 1 (use "*" without space)

option 3: 2 * u * v - 3 * x5 * x6 + 4 * u - x <= 1 (use "*" with space)
```
I notice that the output would be complicated If users use `"*"` or `" "` as part of variable names. Perhaps it's good to warn it when `prettyprint` recognizes `"*"` and `" "` in the variable names. 
woodsp-ibm: For me personally, of what you have above option 2 seems easier to look at. How about adding ( ) to that as below...

```
2 * u * v - 3 * x5 * x6 + 4 * u - x <= 1

(2 * u * v) - (3 * x5 * x6) + (4 * u) - x <= 1
```
For option 3 with all spaces the grouping does not stand out so easily. One could add brackets - makes it a bit more verbose but for me its easier to look at. So that or option 2, though this would help if they have * in the name; though I guess if you really want to cause yourself problems with the output you can have a variable like '-x' as well as one called 'x' then good luck working out what '-x' is in the output. I guess the first one negated would be '--x'!
t-imamichi: Thank you for your idea. I prefer a short format because these strings will be used in debuggers of PyCharm and vscode.
I tried the option 2 in the latest commit and updated the description. https://github.com/Qiskit/qiskit-optimization/pull/366#issue-1214171077
As for problematic symbols (`" ", "-", "+", "*"), which are used in pretty-printed expressions, I tried to display them with `("{name}")`. See the example of the description.

I see Docplex rename such names with the symbols into a different names with a warning message. I think it might be confusing to users. So, I didn't try this approach.
```python
from docplex.mp.model import Model

mod = Model()
mod.binary_var(' x')
mod.binary_var('*x')
mod.binary_var('/x')
mod.binary_var('+x')
mod.binary_var('-x')

print(mod.export_as_lp_string())
```
output
```
Warning: Some identifiers are not valid LP identifiers: 1 (e.g.: "*x")
\ This file has been generated by DOcplex
\ ENCODING=ISO-8859-1
\Problem name: docplex_model1

Minimize
 obj:
Subject To

Bounds
 0 <= _x <= 1
 0 <= x2 <= 1
 0 <= _x#2 <= 1
 0 <= px <= 1
 0 <= mx <= 1

Binaries
 _x x2 _x#2 px mx
End
```
woodsp-ibm: >  I prefer a short format because these strings will be used in debuggers of PyCharm and vscode.

This here though is some more user friendly pretty printed string right - more so than repr or perhaps str. Maybe there will be similarities hence the comment.

I agrees this `"*a*"*"*b*"` is hard on the eyes but does it need to be wrapped with both ( and " ie `("*a*")*("*b*")` which makes it longer but I agree a bit more readable. Maybe it doesn't matter too much as hopefully the user would avoid using those characters in the name anyway so what we are talking about is more of an edge case. We could choose to do nothing special and just note that a poor choice of names, ones that include spaces etc may lead to various string outputs being harder to parse.

In looking at LP format they do have a definition of what a variable name can be https://www.ibm.com/docs/en/icos/12.8.0.0?topic=representation-variable-names-in-lp-file-format hence I guess docplex cannot legitimately put them into the LP output. 
t-imamichi: I tried the corner case with docplex and gurobipy. Docplex has `prettyprint` and `export_as_lp_string`.

Here is a summary of a sum of various corner cases.
```
docplex - prettyprint:  x^3 + 2x + .4 + *x + y%4 + a_m_b + θ + x10 [ 1.2*[5] ];
docplex - lp string:    x1 + x2 + x3 + x6 + y%4 + a_m_b + x9 + x10 + [ 2 x4*x5 ]/2
gurobipy:  <gurobi.QuadExpr: x^3 + 2x + .4 + *x + y+0x1.cp-1072 - b + θ + C9 + [ 1.2 * [5] ]>
```

LP format has various restrictions as you pointed out and it converts many variable names into default one (`x_{}`).
But, pretty-printed format of both docplex and gurobipy transform only some specific corner cases.
I think our pretty-printed format can take a similar approach with docplex and gurobipy, i.e., we leave symbols as is.
But, we have to convert an empty name into a default name.

Btw, gurobipy has a strange output such as `0x1.cp-1072`. They might have an issue with `%`.

-----------------
Details

```python
from docplex.mp.model import Model

mod = Model()

a = mod.binary_var('x^3')
b = mod.binary_var('2x')
c = mod.binary_var('.4')
d = mod.binary_var('1.2')
e = mod.binary_var('[5]')
f = mod.binary_var('*x')
g = mod.binary_var('y%4')
h = mod.binary_var('a - b')
i = mod.binary_var('θ')
j = mod.binary_var('')

mod.minimize(a + b + c + d * e + f + g + h + i +j)

mod.prettyprint()
print('-----------------')
print(mod.export_as_lp_string())
```

output:
```
// This file has been generated by DOcplex
// model name is: docplex_model1
// single vars section
dvar bool x^3;
dvar bool 2x;
dvar bool .4;
dvar bool 1.2;
dvar bool [5];
dvar bool *x;
dvar bool y%4;
dvar bool a_m_b;
dvar bool θ;
dvar bool x10;

minimize
 x^3 + 2x + .4 + *x + y%4 + a_m_b + θ + x10 [ 1.2*[5] ];

subject to {

}
-----------------
Warning: Some identifiers are not valid LP identifiers: 7 (e.g.: "x^3")
\ This file has been generated by DOcplex
\ ENCODING=ISO-8859-1
\Problem name: docplex_model1

Minimize
 obj: x1 + x2 + x3 + x6 + y%4 + a_m_b + x9 + x10 + [ 2 x4*x5 ]/2
Subject To

Bounds
 0 <= x1 <= 1
 0 <= x2 <= 1
 0 <= x3 <= 1
 0 <= x4 <= 1
 0 <= x5 <= 1
 0 <= x6 <= 1
 0 <= y%4 <= 1
 0 <= a_m_b <= 1
 0 <= x9 <= 1
 0 <= x10 <= 1

Binaries
 x1 x2 x3 x4 x5 x6 y%4 a_m_b x9 x10
End
```
-----------------
```python
import gurobipy as gp

mod = gp.Model()

a = mod.addVar(vtype=gp.GRB.BINARY, name='x^3')
b = mod.addVar(vtype=gp.GRB.BINARY, name='2x')
c = mod.addVar(vtype=gp.GRB.BINARY, name='.4')
d = mod.addVar(vtype=gp.GRB.BINARY, name='1.2')
e = mod.addVar(vtype=gp.GRB.BINARY, name='[5]')
f = mod.addVar(vtype=gp.GRB.BINARY, name='*x')
g = mod.addVar(vtype=gp.GRB.BINARY, name='y%4')
h = mod.addVar(vtype=gp.GRB.BINARY, name='a - b')
i = mod.addVar(vtype=gp.GRB.BINARY, name='θ')
j = mod.addVar(vtype=gp.GRB.BINARY, name='')

mod.setObjective(a + b + c + d * e + f + g + h + i + j, gp.GRB.MINIMIZE)
mod.update()
mod.display()
```
output
```
Minimize
  <gurobi.QuadExpr: x^3 + 2x + .4 + *x + y+0x1.cp-1072 - b + θ + C9 + [ 1.2 * [5] ]>
Subject To
Binaries
  ['x^3', '2x', '.4', '1.2', '[5]', '*x', 'y 0x1.ffdefa1fe7p-1028 - b', 'θ', 'C9']
```
t-imamichi: I updated the PR as follows.
- Remove the special care `("{name}")` similar to docplex.prettyprint and gurobipy.dispaly
- Set the default variable name if an empty name is given
t-imamichi: The corner case with this PR

```python
from qiskit_optimization.problems import QuadraticProgram

mod = QuadraticProgram()

a = mod.binary_var('x^3')
b = mod.binary_var('2x')
c = mod.binary_var('.4')
d = mod.binary_var('1.2')
e = mod.binary_var('[5]')
f = mod.binary_var('*x')
g = mod.binary_var('y%4')
h = mod.binary_var('a - b')
i = mod.binary_var('θ')
j = mod.binary_var('')

mod.minimize(linear=[1, 1, 1, 0, 0, 1, 1, 1, 1, 1], quadratic={(3, 4): 1})

print(mod.prettyprint())
```

output
```
Problem name: 

Minimize
  1.2*[5] + *x + .4 + 2x + a - b + x9 + x^3 + y%4 + θ

Subject to
  No constraints

  Binary variables (10)
    x^3 2x .4 1.2 [5] *x y%4 a - b θ x9
```
t-imamichi: I think it's easy for users to avoid such problematic cases (not use white spaces, etc.). So, I'm fine not to include a special care for corner cases similar to docplex.prettyprint and gurobipy.display.
t-imamichi: I updated the warning and error message to include `repr(name)` and simplify the problem name setting.
t-imamichi: I updated the PR 88f28d9 as follows
- Remove `prettyprint` function in `__all__` of translators
- Mention default name in QP`s methods ("if `name` is None or empty, the default name is used").
- Adds `qp.prettyprint` and the unit test of prettyprint.

Patch:
diff --git a/.pylintdict b/.pylintdict
index 3755d95..fdbf66f 100644
--- a/.pylintdict
+++ b/.pylintdict
@@ -143,6 +143,7 @@ pre
 preprint
 prepend
 presolver
+prettyprint
 princeton
 probabilistically
 py
diff --git a/qiskit_optimization/problems/constraint.py b/qiskit_optimization/problems/constraint.py
index bbe0f68..4d7fb85 100644
--- a/qiskit_optimization/problems/constraint.py
+++ b/qiskit_optimization/problems/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
@@ -23,7 +23,7 @@ from ..exceptions import QiskitOptimizationError
 
 
 class ConstraintSense(Enum):
-    """Constants Sense Type."""
+    """Constraint Sense Type."""
 
     # pylint: disable=invalid-name
     LE = 0
@@ -68,6 +68,20 @@ class ConstraintSense(Enum):
         else:
             return ConstraintSense.GE
 
+    @property
+    def label(self) -> str:
+        """Label of the constraint sense
+
+        Returns:
+            The label of the constraint sense ('<=', '>=', or '==')
+        """
+        if self is ConstraintSense.LE:
+            return "<="
+        elif self is ConstraintSense.GE:
+            return ">="
+        else:
+            return "=="
+
 
 class Constraint(QuadraticProgramElement):
     """Abstract Constraint Class."""
diff --git a/qiskit_optimization/problems/quadratic_program.py b/qiskit_optimization/problems/quadratic_program.py
index d172942..7601e1c 100644
--- a/qiskit_optimization/problems/quadratic_program.py
+++ b/qiskit_optimization/problems/quadratic_program.py
@@ -17,15 +17,16 @@ from collections.abc import Sequence
 from enum import Enum
 from math import isclose
 from typing import Dict, List, Optional, Tuple, Union, cast
+from warnings import warn
 
 import numpy as np
 from docplex.mp.model_reader import ModelReader
 from numpy import ndarray
-from scipy.sparse import spmatrix
-
 from qiskit.opflow import OperatorBase
+from scipy.sparse import spmatrix
 
 import qiskit_optimization.optionals as _optionals
+
 from ..exceptions import QiskitOptimizationError
 from ..infinity import INFINITY
 from .constraint import Constraint, ConstraintSense
@@ -59,7 +60,10 @@ class QuadraticProgram:
         Args:
             name: The name of the quadratic program.
         """
-        self._name = name
+        if not name.isprintable():
+            warn("Problem name is not printable")
+        self._name = ""
+        self.name = name
         self._status = QuadraticProgram.Status.VALID
 
         self._variables: List[Variable] = []
@@ -110,6 +114,7 @@ class QuadraticProgram:
         Args:
             name: The name of the quadratic program.
         """
+        self._check_name(name, "Problem")
         self._name = name
 
     @property
@@ -147,7 +152,7 @@ class QuadraticProgram:
         vartype: VarType,
         name: Optional[str],
     ) -> Variable:
-        if name is None:
+        if not name:
             name = "x"
             key_format = "{}"
         else:
@@ -165,7 +170,7 @@ class QuadraticProgram:
     ) -> Tuple[List[str], List[Variable]]:
         if isinstance(keys, int) and keys < 1:
             raise QiskitOptimizationError(f"Cannot create non-positive number of variables: {keys}")
-        if name is None:
+        if not name:
             name = "x"
         if "{{}}" in key_format:
             raise QiskitOptimizationError(
@@ -200,6 +205,7 @@ class QuadraticProgram:
                 indexed_name, k = _find_name(name, key_format, k)
             if indexed_name in self._variables_index:
                 raise QiskitOptimizationError(f"Variable name already exists: {indexed_name}")
+            self._check_name(indexed_name, "Variable")
             names.append(indexed_name)
             self._variables_index[indexed_name] = self.get_num_vars()
             variable = Variable(self, indexed_name, lowerbound, upperbound, vartype)
@@ -291,6 +297,7 @@ class QuadraticProgram:
             lowerbound: The lowerbound of the variable.
             upperbound: The upperbound of the variable.
             name: The name of the variable.
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
 
         Returns:
             The added variable.
@@ -315,6 +322,7 @@ class QuadraticProgram:
             lowerbound: The lower bound of the variable(s).
             upperbound: The upper bound of the variable(s).
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -348,6 +356,7 @@ class QuadraticProgram:
             lowerbound: The lower bound of the variable(s).
             upperbound: The upper bound of the variable(s).
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -371,6 +380,7 @@ class QuadraticProgram:
 
         Args:
             name: The name of the variable.
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
 
         Returns:
             The added variable.
@@ -391,6 +401,7 @@ class QuadraticProgram:
 
         Args:
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -418,6 +429,7 @@ class QuadraticProgram:
 
         Args:
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -446,6 +458,7 @@ class QuadraticProgram:
             lowerbound: The lowerbound of the variable.
             upperbound: The upperbound of the variable.
             name: The name of the variable.
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
 
         Returns:
             The added variable.
@@ -470,6 +483,7 @@ class QuadraticProgram:
             lowerbound: The lower bound of the variable(s).
             upperbound: The upper bound of the variable(s).
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -501,6 +515,7 @@ class QuadraticProgram:
             lowerbound: The lower bound of the variable(s).
             upperbound: The upper bound of the variable(s).
             name: The name(s) of the variable(s).
+                If it's ``None`` or empty ``""``, the default name, e.g., ``x0``, is used.
             key_format: The format used to name/index the variable(s).
             keys: If keys: int, it is interpreted as the number of variables to construct.
                   Otherwise, the elements of the sequence are converted to strings via 'str' and
@@ -607,6 +622,7 @@ class QuadraticProgram:
 
             rhs: The right-hand side of the constraint.
             name: The name of the constraint.
+                If it's ``None`` or empty ``""``, the default name, e.g., ``c0``, is used.
 
         Returns:
             The added constraint.
@@ -618,6 +634,7 @@ class QuadraticProgram:
         if name:
             if name in self.linear_constraints_index:
                 raise QiskitOptimizationError(f"Linear constraint's name already exists: {name}")
+            self._check_name(name, "Linear constraint")
         else:
             k = self.get_num_linear_constraints()
             while f"c{k}" in self.linear_constraints_index:
@@ -701,6 +718,7 @@ class QuadraticProgram:
 
             rhs: The right-hand side of the constraint.
             name: The name of the constraint.
+                If it's ``None`` or empty ``""``, the default name, e.g., ``q0``, is used.
 
         Returns:
             The added constraint.
@@ -711,6 +729,7 @@ class QuadraticProgram:
         if name:
             if name in self.quadratic_constraints_index:
                 raise QiskitOptimizationError(f"Quadratic constraint name already exists: {name}")
+            self._check_name(name, "Quadratic constraint")
         else:
             k = self.get_num_quadratic_constraints()
             while f"q{k}" in self.quadratic_constraints_index:
@@ -1072,3 +1091,23 @@ class QuadraticProgram:
         feasible, _, _ = self.get_feasibility_info(x)
 
         return feasible
+
+    def prettyprint(self) -> str:
+        """Returns a pretty printed string of this problem.
+
+        Returns:
+            A pretty printed string representing the problem.
+
+        Raises:
+            QiskitOptimizationError: if there is a non-printable name.
+        """
+        # pylint: disable=cyclic-import
+        from qiskit_optimization.translators.prettyprint import prettyprint
+
+        return prettyprint(self)
+
+    @staticmethod
+    def _check_name(name: str, name_type: str) -> None:
+        """Displays a warning message if a name string is not printable"""
+        if not name.isprintable():
+            warn(f"{name_type} name is not printable: {repr(name)}")
diff --git a/qiskit_optimization/translators/__init__.py b/qiskit_optimization/translators/__init__.py
index 3106810..065dae0 100644
--- a/qiskit_optimization/translators/__init__.py
+++ b/qiskit_optimization/translators/__init__.py
@@ -1,6 +1,6 @@
 # This code is part of Qiskit.
 #
-# (C) Copyright IBM 2021.
+# (C) Copyright IBM 2021, 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
@@ -17,7 +17,7 @@ Quadratic program translators (:mod:`qiskit_optimization.translators`)
 .. currentmodule:: qiskit_optimization.translators
 
 Translators between :class:`~qiskit_optimization.problems.QuadraticProgram` and
-other optimization models.
+other optimization models or other objects.
 
 Translators
 ======================
@@ -37,7 +37,7 @@ from .docplex_mp import from_docplex_mp, to_docplex_mp
 from .gurobipy import from_gurobipy, to_gurobipy
 from .ising import from_ising, to_ising
 
-_all = [
+__all__ = [
     "from_docplex_mp",
     "to_docplex_mp",
     "from_gurobipy",
diff --git a/qiskit_optimization/translators/prettyprint.py b/qiskit_optimization/translators/prettyprint.py
new file mode 100644
index 0000000..96b7ee4
--- /dev/null
+++ b/qiskit_optimization/translators/prettyprint.py
@@ -0,0 +1,251 @@
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 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
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Translate ``QuadraticProgram`` into a pretty-printed string"""
+
+from io import StringIO
+from math import isclose
+from typing import Optional, Union, cast
+
+import numpy as np
+
+from qiskit_optimization import INFINITY, QiskitOptimizationError
+from qiskit_optimization.problems import (
+    LinearExpression,
+    QuadraticExpression,
+    QuadraticObjective,
+    QuadraticProgram,
+    VarType,
+)
+
+DEFAULT_TRUNCATE = 50
+
+
+def _int_if_close(val: Union[int, float, np.integer, np.floating]) -> Union[int, float]:
+    """Convert a value into an integer if possible
+
+    Note: if abs(val) is too large, int(val) is not correct
+          e.g., int(1e16 - 1) -> 10000000000000000
+    """
+    if isinstance(val, np.integer):
+        val = int(val)
+    elif isinstance(val, np.floating):
+        val = float(val)
+
+    if isinstance(val, int):
+        return val
+    if abs(val) <= 1e10 and val.is_integer():
+        return int(val)
+    return val
+
+
+def _term2str(coeff: float, term: str, is_head: bool) -> str:
+    """Translate a pair of a coefficient and a term to a string.
+
+    Args:
+        coeff: a coefficient.
+        term: a term. This can be empty and `coeff` is treated as a constant.
+        is_head: Whether this coefficient appears in the head of the string or not.
+
+    Returns:
+        A strings representing the term.
+    """
+    if term:
+        if is_head:
+            if isclose(coeff, 1.0):
+                ret = term
+            elif isclose(coeff, -1.0):
+                ret = f"-{term}"
+            else:
+                ret = f"{_int_if_close(coeff)}*{term}"
+        else:
+            sign = "-" if coeff < 0.0 else "+"
+            abs_val = abs(coeff)
+            if isclose(abs_val, 1.0):
+                ret = f" {sign} {term}"
+            else:
+                ret = f" {sign} {_int_if_close(abs_val)}*{term}"
+    else:
+        if is_head:
+            ret = f"{_int_if_close(coeff)}"
+        else:
+            sign = "-" if coeff < 0.0 else "+"
+            abs_val = abs(coeff)
+            ret = f" {sign} {_int_if_close(abs_val)}"
+    return ret
+
+
+def _check_name(name: str, name_type: str) -> None:
+    """Check a name is printable or not.
+
+    Args:
+        name: a variable name.
+        name_type: the type associated with the name.
+
+    Raises:
+        QiskitOptimizationError: if the name is not printable.
+    """
+    if not name.isprintable():
+        raise QiskitOptimizationError(f"{name_type} name is not printable: {repr(name)}")
+
+
+def _expr2str(
+    constant: float = 0.0,
+    linear: Optional[LinearExpression] = None,
+    quadratic: Optional[QuadraticExpression] = None,
+    truncate: int = 0,
+) -> str:
+    """Translate a combination of a constant, a linear expression, and a quadratic expression
+    into a string.
+
+    Args:
+        constant: a constant part.
+        linear: a linear expression.
+        quadratic: a quadratic expression.
+        truncate: the threshold of the output string to be truncated. If a string is longer than
+            the threshold, it is truncated and appended "...", e.g., "x^2 + y +...".
+            The default value 0 means no truncation is carried out.
+
+    Returns:
+        A string representing the combination of the expressions.
+
+    Raises:
+        ValueError: if `truncate` is negative.
+        QiskitOptimizationError: if the variable name is not printable.
+    """
+    if truncate < 0:
+        raise ValueError(f"Invalid truncate value: {truncate}")
+
+    expr = StringIO()
+    is_head = True
+    lin_dict = linear.to_dict(use_name=True) if linear else {}
+    quad_dict = quadratic.to_dict(use_name=True) if quadratic else {}
+
+    # quadratic expression
+    for (var1, var2), coeff in sorted(quad_dict.items()):
+        _check_name(cast(str, var1), "Variable")
+        _check_name(cast(str, var2), "Variable")
+        if var1 == var2:
+            expr.write(_term2str(coeff, f"{var1}^2", is_head))
+        else:
+            expr.write(_term2str(coeff, f"{var1}*{var2}", is_head))
+        is_head = False
+
+    # linear expression
+    for var, coeff in sorted(lin_dict.items()):
+        _check_name(cast(str, var), "Variable")
+        expr.write(_term2str(coeff, f"{var}", is_head))
+        is_head = False
+
+    # constant
+    if not isclose(constant, 0.0, abs_tol=1e-10):
+        expr.write(_term2str(constant, "", is_head))
+    elif not lin_dict and not quad_dict:
+        expr.write(_term2str(0, "", is_head))
+
+    ret = expr.getvalue()
+    if 0 < truncate < len(ret):
+        ret = ret[:truncate] + "..."
+    return ret
+
+
+def prettyprint(quadratic_program: QuadraticProgram) -> str:
+    """Translate a :class:`~qiskit_optimization.problems.QuadraticProgram` into a pretty-printed string.
+
+    Args:
+        quadratic_program: The optimization problem to be translated into a string
+
+    Returns:
+        A pretty-printed string representing the problem.
+
+    Raises:
+        QiskitOptimizationError: if there is a non-printable name.
+    """
+
+    with StringIO() as buf:
+        _check_name(quadratic_program.name, "Problem")
+        buf.write(f"Problem name: {quadratic_program.name}\n\n")
+        if quadratic_program.objective.sense == QuadraticObjective.Sense.MINIMIZE:
+            buf.write("Minimize\n")
+        else:
+            buf.write("Maximize\n")
+        buf.write("  ")
+        buf.write(
+            _expr2str(
+                quadratic_program.objective.constant,
+                quadratic_program.objective.linear,
+                quadratic_program.objective.quadratic,
+            )
+        )
+        buf.write("\n\nSubject to")
+        num_lin_csts = quadratic_program.get_num_linear_constraints()
+        num_quad_csts = quadratic_program.get_num_quadratic_constraints()
+        if num_lin_csts == 0 and num_quad_csts == 0:
+            buf.write("\n  No constraints\n")
+        if num_lin_csts > 0:
+            buf.write(f"\n  Linear constraints ({num_lin_csts})\n")
+            for cst in quadratic_program.linear_constraints:
+                _check_name(cst.name, "Linear constraint")
+                buf.write(
+                    f"    {_expr2str(linear=cst.linear)}"
+                    f" {cst.sense.label} {_int_if_close(cst.rhs)}"
+                    f"  '{cst.name}'\n"
+                )
+        if num_quad_csts > 0:
+            buf.write(f"\n  Quadratic constraints ({num_quad_csts})\n")
+            for cst2 in quadratic_program.quadratic_constraints:
+                _check_name(cst2.name, "Quadratic constraint")
+                buf.write(
+                    f"    {_expr2str(linear=cst2.linear, quadratic=cst2.quadratic)}"
+                    f" {cst2.sense.label} {_int_if_close(cst2.rhs)}"
+                    f"  '{cst2.name}'\n"
+                )
+        if quadratic_program.get_num_vars() == 0:
+            buf.write("\n  No variables\n")
+        bin_vars = []
+        int_vars = []
+        con_vars = []
+        for var in quadratic_program.variables:
+            if var.vartype is VarType.BINARY:
+                _check_name(var.name, "Variable")
+                bin_vars.append(var.name)
+            elif var.vartype is VarType.INTEGER:
+                int_vars.append(var)
+            else:
+                con_vars.append(var)
+        if int_vars:
+            buf.write(f"\n  Integer variables ({len(int_vars)})\n")
+            for var in int_vars:
+                buf.write("    ")
+                if var.lowerbound > -INFINITY:
+                    buf.write(f"{_int_if_close(var.lowerbound)} <= ")
+                _check_name(var.name, "Variable")
+                buf.write(var.name)
+                if var.upperbound < INFINITY:
+                    buf.write(f" <= {_int_if_close(var.upperbound)}")
+                buf.write("\n")
+        if con_vars:
+            buf.write(f"\n  Continuous variables ({len(con_vars)})\n")
+            for var in con_vars:
+                buf.write("    ")
+                if var.lowerbound > -INFINITY:
+                    buf.write(f"{_int_if_close(var.lowerbound)} <= ")
+                _check_name(var.name, "Variable")
+                buf.write(var.name)
+                if var.upperbound < INFINITY:
+                    buf.write(f" <= {_int_if_close(var.upperbound)}")
+                buf.write("\n")
+        if bin_vars:
+            buf.write(f"\n  Binary variables ({len(bin_vars)})\n")
+            buf.write(f"    {' '.join(bin_vars)}\n")
+        ret = buf.getvalue()
+    return ret
diff --git a/releasenotes/notes/add-prettyprint-8ec601f41c9cc7d2.yaml b/releasenotes/notes/add-prettyprint-8ec601f41c9cc7d2.yaml
new file mode 100644
index 0000000..b0eeb01
--- /dev/null
+++ b/releasenotes/notes/add-prettyprint-8ec601f41c9cc7d2.yaml
@@ -0,0 +1,86 @@
+---
+upgrade:
+  - |
+    Shows a warning message if non-printable strings are set to
+    :class:`~qiskit_optimization.QuadraticProgram`
+    as problem name, variable name, or constraint name.
+  - |
+    If users set an empty variable name ``""`` with
+    :meth:`~qiskit_optimization.QuadraticProgram.binary_var`,
+    :meth:`~qiskit_optimization.QuadraticProgram.integer_var`, and
+    :meth:`~qiskit_optimization.QuadraticProgram.continuous_var`,
+    they set the default variable name (e.g., ``x0``)
+    while they used to set the empty name as variable name.
+
+    .. code-block:: python
+
+        from qiskit_optimization.problems import QuadraticProgram
+
+        qp = QuadraticProgram()
+        x = qp.binary_var(name="")
+        y = qp.integer_var(name="")
+        z = qp.continuous_var(name="")
+        print(x.name)  # x0
+        print(y.name)  # x1
+        print(z.name)  # x2
+
+features:
+  - |
+    Adds a method :meth:`~qiskit_optimization.QuadraticProgram.prettyprint`
+    to :class:`~qiskit_optimization.QuadraticProgram` to generate a pretty-printed string of the object.
+
+    Here is an example of pretty printing.
+
+    .. code-block:: python
+
+        from qiskit_optimization import QuadraticProgram
+
+        qp = QuadraticProgram('problem 1')
+        qp.integer_var(-1, 2, 'x')
+        qp.integer_var(-1, 2, 'y')
+        qp.continuous_var(-1, name='z')
+        qp.binary_var('u')
+        qp.binary_var('v')
+        qp.binary_var_list(10)
+        qp.integer_var_list(3)
+        qp.continuous_var_list(3)
+        qp.minimize(constant=3, linear={'x': 2, 'y': 3}, quadratic={('u', 'x'): -1})
+        qp.linear_constraint({'x': 1, 'y': -2}, '>=', 2, name='lin_GE')
+        qp.linear_constraint({'x': 2, 'y': -1}, '==', 1, name='lin_EQ')
+        qp.quadratic_constraint({'x': 1, 'u': 1}, {(3, 4): 1, (5, 6): -1}, '<=', 1, name='quad_LE')
+        qp.quadratic_constraint({'x': 2, 'y': -1}, {('z', 'z'): -1}, '<=', 1)
+
+        print(qp.prettyprint())
+
+    The output is as follows. ::
+
+        Problem name: problem 1
+
+        Minimize
+          -x*u + 2*x + 3*y + 3
+
+        Subject to
+          Linear constraints (2)
+            x - 2*y >= 2  'lin_GE'
+            2*x - y == 1  'lin_EQ'
+
+          Quadratic constraints (2)
+            u*v - x5*x6 + u + x <= 1  'quad_LE'
+            -z^2 + 2*x - y <= 1  'q1'
+
+          Integer variables (5)
+            -1 <= x <= 2
+            -1 <= y <= 2
+            0 <= x15
+            0 <= x16
+            0 <= x17
+
+          Continuous variables (4)
+            -1 <= z
+            0 <= x18
+            0 <= x19
+            0 <= x20
+
+          Binary variables (12)
+            u v x5 x6 x7 x8 x9 x10 x11 x12 x13 x14
+


Test patch:
diff --git a/test/problems/test_constraint.py b/test/problems/test_constraint.py
new file mode 100644
index 0000000..e9923bc
--- /dev/null
+++ b/test/problems/test_constraint.py
@@ -0,0 +1,55 @@
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 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
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+""" Test Constraint """
+
+from test.optimization_test_case import QiskitOptimizationTestCase
+
+from qiskit_optimization.problems import Constraint, QuadraticProgram
+from qiskit_optimization.problems.constraint import ConstraintSense
+
+
+class TestConstraintSense(QiskitOptimizationTestCase):
+    """Test ConstraintSense."""
+
+    def setUp(self) -> None:
+        super().setUp()
+        self._sense = {
+            ConstraintSense.EQ: ["==", "=", "E", "EQ"],
+            ConstraintSense.LE: ["<=", "<", "L", "LE"],
+            ConstraintSense.GE: [">=", ">", "G", "GE"],
+        }
+
+    def test_convert(self):
+        """test convert."""
+        for sense, lst in self._sense.items():
+            for label in lst:
+                self.assertEqual(ConstraintSense.convert(label), sense)
+
+    def test_label(self):
+        """test label."""
+        for sense, lst in self._sense.items():
+            self.assertEqual(sense.label, lst[0])
+
+
+class TestConstraint(QiskitOptimizationTestCase):
+    """Test Constraint"""
+
+    def test_init(self):
+        """test init"""
+        quadratic_program = QuadraticProgram("test")
+        for sense in [ConstraintSense.GE, ConstraintSense.LE, ConstraintSense.EQ]:
+            cst = Constraint(quadratic_program, "name", sense, 1.0)
+            self.assertEqual(cst.name, "name")
+            self.assertEqual(cst.sense, sense)
+            self.assertAlmostEqual(cst.rhs, 1.0)
+            self.assertEqual(cst.quadratic_program.name, "test")
diff --git a/test/problems/test_quadratic_program.py b/test/problems/test_quadratic_program.py
index 55e7ab9..caddbc5 100644
--- a/test/problems/test_quadratic_program.py
+++ b/test/problems/test_quadratic_program.py
@@ -18,8 +18,8 @@ from os import path
 from test.optimization_test_case import QiskitOptimizationTestCase
 
 from docplex.mp.model import DOcplexException
-
 from qiskit.opflow import PauliSumOp
+
 import qiskit_optimization.optionals as _optionals
 from qiskit_optimization import INFINITY, QiskitOptimizationError, QuadraticProgram
 from qiskit_optimization.problems import Constraint, QuadraticObjective, Variable, VarType
@@ -172,7 +172,7 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
             q_p.binary_var("x_")
 
         d_5 = q_p.continuous_var_dict(1, -1, 2, "", "")
-        self.assertSetEqual(set(d_5.keys()), {""})
+        self.assertSetEqual(set(d_5.keys()), {"x"})
         self.assertSetEqual(
             {var.name for var in q_p.variables},
             {
@@ -189,7 +189,7 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
                 "c1",
                 "c2",
                 "x_",
-                "",
+                "x",
             },
         )
         for var in q_p.variables[-1:]:
@@ -201,9 +201,6 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
         with self.assertRaises(QiskitOptimizationError):
             q_p.binary_var_dict(1, "", "")
 
-        with self.assertRaises(QiskitOptimizationError):
-            q_p.integer_var(0, 1, "")
-
         with self.assertRaises(QiskitOptimizationError):
             q_p = QuadraticProgram()
             q_p.binary_var_dict(keys=1, key_format="{}{}")
@@ -327,7 +324,7 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
             q_p.binary_var("x_")
 
         d_5 = q_p.integer_var_list(1, -1, 2, "", "")
-        names = [""]
+        names = ["x"]
         self.assertSetEqual(
             {var.name for var in q_p.variables},
             {
@@ -344,7 +341,7 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
                 "c1",
                 "c2",
                 "x_",
-                "",
+                "x",
             },
         )
         for i, var in enumerate(q_p.variables[-1:]):
@@ -357,9 +354,6 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
         with self.assertRaises(QiskitOptimizationError):
             q_p.binary_var_list(1, "", "")
 
-        with self.assertRaises(QiskitOptimizationError):
-            q_p.integer_var(0, 1, "")
-
         with self.assertRaises(QiskitOptimizationError):
             q_p = QuadraticProgram()
             q_p.binary_var_list(keys=1, key_format="{}{}")
@@ -1031,6 +1025,107 @@ class TestQuadraticProgram(QiskitOptimizationTestCase):
         except RuntimeError as ex:
             self.fail(str(ex))
 
+    def test_empty_name(self):
+        """Test empty names"""
+
+        with self.subTest("problem name"):
+            q_p = QuadraticProgram("")
+            self.assertEqual(q_p.name, "")
+
+        with self.subTest("variable name"):
+            q_p = QuadraticProgram()
+            x = q_p.binary_var(name="")
+            y = q_p.integer_var(name="")
+            z = q_p.continuous_var(name="")
+            self.assertEqual(x.name, "x0")
+            self.assertEqual(y.name, "x1")
+            self.assertEqual(z.name, "x2")
+
+        with self.subTest("variable name 2"):
+            q_p = QuadraticProgram()
+            w = q_p.binary_var(name="w")
+            x = q_p.binary_var(name="")
+            y = q_p.integer_var(name="")
+            z = q_p.continuous_var(name="")
+            self.assertEqual(w.name, "w")
+            self.assertEqual(x.name, "x1")
+            self.assertEqual(y.name, "x2")
+            self.assertEqual(z.name, "x3")
+
+        with self.subTest("variable name list"):
+            q_p = QuadraticProgram()
+            x = q_p.binary_var_list(2, name="")
+            y = q_p.integer_var_list(2, name="")
+            z = q_p.continuous_var_list(2, name="")
+            self.assertListEqual([v.name for v in x], ["x0", "x1"])
+            self.assertListEqual([v.name for v in y], ["x2", "x3"])
+            self.assertListEqual([v.name for v in z], ["x4", "x5"])
+
+        with self.subTest("variable name dict"):
+            q_p = QuadraticProgram()
+            x = q_p.binary_var_dict(2, name="")
+            y = q_p.integer_var_dict(2, name="")
+            z = q_p.continuous_var_dict(2, name="")
+            self.assertDictEqual({k: v.name for k, v in x.items()}, {"x0": "x0", "x1": "x1"})
+            self.assertDictEqual({k: v.name for k, v in y.items()}, {"x2": "x2", "x3": "x3"})
+            self.assertDictEqual({k: v.name for k, v in z.items()}, {"x4": "x4", "x5": "x5"})
+
+        with self.subTest("linear constraint name"):
+            q_p = QuadraticProgram()
+            x = q_p.linear_constraint(name="")
+            y = q_p.linear_constraint(name="")
+            self.assertEqual(x.name, "c0")
+            self.assertEqual(y.name, "c1")
+
+        with self.subTest("quadratic constraint name"):
+            q_p = QuadraticProgram()
+            x = q_p.quadratic_constraint(name="")
+            y = q_p.quadratic_constraint(name="")
+            self.assertEqual(x.name, "q0")
+            self.assertEqual(y.name, "q1")
+
+    def test_printable_name(self):
+        """Test non-printable names"""
+        name = "\n"
+
+        with self.assertWarns(UserWarning):
+            _ = QuadraticProgram(name)
+
+        q_p = QuadraticProgram()
+
+        with self.assertWarns(UserWarning):
+            q_p.binary_var(name + "bin")
+
+        with self.assertWarns(UserWarning):
+            q_p.binary_var_list(10, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.binary_var_dict(10, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.integer_var(0, 1, name + "int")
+
+        with self.assertWarns(UserWarning):
+            q_p.integer_var_list(10, 0, 1, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.integer_var_dict(10, 0, 1, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.continuous_var(0, 1, name + "cont")
+
+        with self.assertWarns(UserWarning):
+            q_p.continuous_var_list(10, 0, 1, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.continuous_var_dict(10, 0, 1, name)
+
+        with self.assertWarns(UserWarning):
+            q_p.linear_constraint(name=name)
+
+        with self.assertWarns(UserWarning):
+            q_p.quadratic_constraint(name=name)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/test/translators/test_prettyprint.py b/test/translators/test_prettyprint.py
new file mode 100644
index 0000000..5685bc0
--- /dev/null
+++ b/test/translators/test_prettyprint.py
@@ -0,0 +1,306 @@
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 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
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+"""Test prettyprint"""
+
+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
+
+
+class TestPrettyprint(QiskitOptimizationTestCase):
+    """Test prettyprint"""
+
+    @staticmethod
+    def _convert(out: str):
+        print('"\\n".join([')
+        for line in out.split("\n"):
+            print(f'"{line}",')
+        print("])")
+
+    def test_prettyprint(self):
+        """test prettyprint"""
+
+        with self.subTest("empty"):
+            q_p = QuadraticProgram()
+            expected = "\n".join(
+                [
+                    "Problem name: ",
+                    "",
+                    "Minimize",
+                    "  0",
+                    "",
+                    "Subject to",
+                    "  No constraints",
+                    "",
+                    "  No variables",
+                    "",
+                ]
+            )
+            self.assertEqual(prettyprint(q_p), expected)
+            self.assertEqual(q_p.prettyprint(), expected)
+
+        with self.subTest("minimize 1"):
+            q_p = QuadraticProgram("test")
+            q_p.binary_var(name="x")
+            q_p.binary_var(name="y")
+            q_p.minimize(linear={"x": 1}, quadratic={("x", "y"): 1})
+            expected = "\n".join(
+                [
+                    "Problem name: test",
+                    "",
+                    "Minimize",
+                    "  x*y + x",
+                    "",
+                    "Subject to",
+                    "  No constraints",
+                    "",
+                    "  Binary variables (2)",
+                    "    x y",
+                    "",
+                ]
+            )
+            self.assertEqual(prettyprint(q_p), expected)
+            self.assertEqual(q_p.prettyprint(), expected)
+
+        with self.subTest("maximize 1"):
+            q_p = QuadraticProgram("test")
+            q_p.integer_var(lowerbound=5, upperbound=10, name="x")
+            q_p.continuous_var(lowerbound=-1, name="y")
+            q_p.maximize(linear={"x": -1, "y": 2}, constant=3)
+            q_p.quadratic_constraint({}, {("x", "y"): -1}, "<=", 4)
+            expected = "\n".join(
+                [
+                    "Problem name: test",
+                    "",
+                    "Maximize",
+                    "  -x + 2*y + 3",
+                    "",
+                    "Subject to",
+                    "  Quadratic constraints (1)",
+                    "    -x*y <= 4  'q0'",
+                    "",
+                    "  Integer variables (1)",
+                    "    5 <= x <= 10",
+                    "",
+                    "  Continuous variables (1)",
+                    "    -1 <= y",
+                    "",
+                ]
+            )
+            self.assertEqual(prettyprint(q_p), expected)
+            self.assertEqual(q_p.prettyprint(), expected)
+
+        with self.subTest("minimize 2"):
+            q_p = QuadraticProgram("my problem")
+            q_p.binary_var("x")
+            q_p.integer_var(-1, 5, "y")
+            q_p.continuous_var(-1, 5, "z")
+            q_p.minimize(1, {"x": 1, "y": -1, "z": 10}, {("x", "x"): 0.5, ("y", "z"): -1})
+            q_p.linear_constraint({"x": 1, "y": 2}, "==", 1, "lin_eq")
+            q_p.linear_constraint({"x": 1, "y": 2}, "<=", 1, "lin_leq")
+            q_p.linear_constraint({"x": 1, "y": 2}, ">=", 1, "lin_geq")
+            q_p.quadratic_constraint(
+                {"x": 1, "y": 1},
+                {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+                "==",
+                1,
+                "quad_eq",
+            )
+            q_p.quadratic_constraint(
+                {"x": 1, "y": 1},
+                {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+                "<=",
+                1,
+                "quad_leq",
+            )
+            q_p.quadratic_constraint(
+                {"x": 1, "y": 1},
+                {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2},
+                ">=",
+                1,
+                "quad_geq",
+            )
+            expected = "\n".join(
+                [
+                    "Problem name: my problem",
+                    "",
+                    "Minimize",
+                    "  0.5*x^2 - y*z + x - y + 10*z + 1",
+                    "",
+                    "Subject to",
+                    "  Linear constraints (3)",
+                    "    x + 2*y == 1  'lin_eq'",
+                    "    x + 2*y <= 1  'lin_leq'",
+                    "    x + 2*y >= 1  'lin_geq'",
+                    "",
+                    "  Quadratic constraints (3)",
+                    "    x^2 - y*z + 2*z^2 + x + y == 1  'quad_eq'",
+                    "    x^2 - y*z + 2*z^2 + x + y <= 1  'quad_leq'",
+                    "    x^2 - y*z + 2*z^2 + x + y >= 1  'quad_geq'",
+                    "",
+                    "  Integer variables (1)",
+                    "    -1 <= y <= 5",
+                    "",
+                    "  Continuous variables (1)",
+                    "    -1 <= z <= 5",
+                    "",
+                    "  Binary variables (1)",
+                    "    x",
+                    "",
+                ]
+            )
+            self.assertEqual(prettyprint(q_p), expected)
+            self.assertEqual(q_p.prettyprint(), expected)
+
+        with self.subTest("maximize 2"):
+            q_p = QuadraticProgram("problem 1")
+            q_p.integer_var(-1, 2, "x")
+            q_p.integer_var(-1, 2, "y")
+            q_p.continuous_var(-1, name="z")
+            q_p.binary_var("u")
+            q_p.binary_var("very_long_variable")
+            q_p.binary_var_list(10)
+            q_p.integer_var_list(3)
+            q_p.continuous_var_list(3)
+            q_p.minimize(constant=3, linear={"x": 2, "y": 3}, quadratic={("u", "x"): -1})
+            q_p.linear_constraint({"x": 1, "y": -2}, ">=", 2, name="lin_GE")
+            q_p.linear_constraint({"x": 2, "y": -1}, "==", 1, name="lin_EQ")
+            q_p.quadratic_constraint(
+                {"x": 1, "u": 1}, {(3, 4): 1, (5, 6): -1}, "<=", 1, name="quad_LE"
+            )
+            q_p.quadratic_constraint({"x": 2, "y": -1}, {("z", "z"): -1}, "<=", 1)
+            expected = "\n".join(
+                [
+                    "Problem name: problem 1",
+                    "",
+                    "Minimize",
+                    "  -x*u + 2*x + 3*y + 3",
+                    "",
+                    "Subject to",
+                    "  Linear constraints (2)",
+                    "    x - 2*y >= 2  'lin_GE'",
+                    "    2*x - y == 1  'lin_EQ'",
+                    "",
+                    "  Quadratic constraints (2)",
+                    "    u*very_long_variable - x5*x6 + u + x <= 1  'quad_LE'",
+                    "    -z^2 + 2*x - y <= 1  'q1'",
+                    "",
+                    "  Integer variables (5)",
+                    "    -1 <= x <= 2",
+                    "    -1 <= y <= 2",
+                    "    0 <= x15",
+                    "    0 <= x16",
+                    "    0 <= x17",
+                    "",
+                    "  Continuous variables (4)",
+                    "    -1 <= z",
+                    "    0 <= x18",
+                    "    0 <= x19",
+                    "    0 <= x20",
+                    "",
+                    "  Binary variables (12)",
+                    "    u very_long_variable x5 x6 x7 x8 x9 x10 x11 x12 x13 x14",
+                    "",
+                ]
+            )
+            self.assertEqual(prettyprint(q_p), expected)
+            self.assertEqual(q_p.prettyprint(), expected)
+
+    def test_prettyprint2(self):
+        """test prettyprint with special characters (' ', '+', '-', '*')"""
+        q_p = QuadraticProgram("+ test - test *")
+        q_p.binary_var(" x ")
+        q_p.continuous_var(1e-10, 1e10, "+y+")
+        q_p.continuous_var(-1e10, -1e-10, "-y-")
+        q_p.integer_var(10, 100, "*z*")
+        q_p.minimize(1, [1, 2, 3, 4])
+        expected = "\n".join(
+            [
+                "Problem name: + test - test *",
+                "",
+                "Minimize",
+                "   x  + 4**z* + 2*+y+ + 3*-y- + 1",
+                "",
+                "Subject to",
+                "  No constraints",
+                "",
+                "  Integer variables (1)",
+                "    10 <= *z* <= 100",
+                "",
+                "  Continuous variables (2)",
+                "    1e-10 <= +y+ <= 10000000000",
+                "    -10000000000 <= -y- <= -1e-10",
+                "",
+                "  Binary variables (1)",
+                "     x ",
+                "",
+            ]
+        )
+        self.assertEqual(prettyprint(q_p), expected)
+        self.assertEqual(q_p.prettyprint(), expected)
+
+    def test_error(self):
+        """Test error case due to non-printable names"""
+
+        name = "\n"
+        with self.subTest("problem name - func"), self.assertRaises(QiskitOptimizationError):
+            q_p = QuadraticProgram(name)
+            _ = prettyprint(q_p)
+
+        with self.subTest("problem name - meth"), self.assertRaises(QiskitOptimizationError):
+            q_p = QuadraticProgram(name)
+            _ = q_p.prettyprint()
+
+        with self.subTest("linear variable name - func"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.binary_var(name)
+            _ = prettyprint(q_p)
+
+        with self.subTest("linear variable name - meth"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.binary_var(name)
+            _ = q_p.prettyprint()
+
+        with self.subTest("linear constraint name - func"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.linear_constraint(name=name)
+            _ = prettyprint(q_p)
+
+        with self.subTest("linear constraint name - meth"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.linear_constraint(name=name)
+            _ = q_p.prettyprint()
+
+        with self.subTest("quadratic constraint name - func"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.quadratic_constraint(name=name)
+            _ = prettyprint(q_p)
+
+        with self.subTest("quadratic constraint name - meth"), self.assertRaises(
+            QiskitOptimizationError
+        ):
+            q_p = QuadraticProgram()
+            q_p.quadratic_constraint(name=name)
+            _ = q_p.prettyprint()