def _process_expr(self): self._num_vars = self._expr.degree ast = self._expr.to_cnf().to_ast() ast = LogicExpressionOracle._normalize_literal_indices( ast, self._expr.usupport) if self._optimization == 'off': self._nf = CNF(ast, num_vars=self._num_vars) else: # self._optimization == 'espresso': expr_dnf = self._expr.to_dnf() if expr_dnf.is_zero() or expr_dnf.is_one(): self._nf = CNF(('const', 0 if expr_dnf.is_zero() else 1), num_vars=self._num_vars) else: expr_dnf_m = espresso_exprs(expr_dnf)[0] expr_dnf_m_ast = LogicExpressionOracle._normalize_literal_indices( expr_dnf_m.to_ast(), expr_dnf_m.usupport) if isinstance(expr_dnf_m, AndOp): self._nf = CNF(expr_dnf_m_ast, num_vars=self._num_vars) elif isinstance(expr_dnf_m, OrOp): self._nf = DNF(expr_dnf_m_ast, num_vars=self._num_vars) else: raise AquaError( 'Unexpected espresso optimization result expr: {}'. format(expr_dnf_m))
class LogicExpressionOracle(Oracle): CONFIGURATION = { 'name': 'LogicExpression', 'description': 'Logic Expression Oracle', 'input_schema': { '$schema': 'http://json-schema.org/schema#', 'id': 'logic_expr_oracle_schema', 'type': 'object', 'properties': { 'expression': { 'type': 'string', }, "optimization": { "type": "string", "default": "espresso", 'oneOf': [{ 'enum': ['off', 'espresso'] }] }, 'mct_mode': { 'type': 'string', 'default': 'basic', 'oneOf': [{ 'enum': ['basic', 'advanced', 'noancilla'] }] }, }, 'additionalProperties': False } } def __init__(self, expression=None, optimization='off', mct_mode='basic'): """ Constructor. Args: expression (str): The string of the desired logic expression. It could be either in the DIMACS CNF format, or a general boolean logic expression, such as 'a ^ b' and 'v[0] & (~v[1] | v[2])' optimization (str): The mode of optimization to use for minimizing the circuit. Currently, besides no optimization ('off'), Aqua also supports an 'espresso' mode <https://en.wikipedia.org/wiki/Espresso_heuristic_logic_minimizer> mct_mode (str): The mode to use for building Multiple-Control Toffoli. """ self.validate(locals()) super().__init__() self._mct_mode = mct_mode self._optimization = optimization if expression is None: raw_expr = expr(None) else: try: raw_expr = expr(expression) except: try: raw_expr = ast2expr( parse_cnf(expression.strip(), varname='v')) except: raise AquaError( 'Failed to parse the input expression: {}.'.format( expression)) self._expr = raw_expr self._process_expr() self.construct_circuit() @staticmethod def _normalize_literal_indices(raw_ast, raw_indices): idx_mapping = { r: i + 1 for r, i in zip(sorted(raw_indices), range(len(raw_indices))) } if raw_ast[0] == 'and' or raw_ast[0] == 'or': clauses = [] for c in raw_ast[1:]: if c[0] == 'lit': clauses.append(('lit', (idx_mapping[c[1]]) if c[1] > 0 else (-idx_mapping[-c[1]]))) elif (c[0] == 'or' or c[0] == 'and') and (raw_ast[0] != c[0]): clause = [] for l in c[1:]: clause.append( ('lit', (idx_mapping[l[1]]) if l[1] > 0 else (-idx_mapping[-l[1]]))) clauses.append((c[0], *clause)) else: raise AquaError( 'Unrecognized logic expression: {}'.format(raw_ast)) elif raw_ast[0] == 'const': return raw_ast else: raise AquaError('Unrecognized root expression type: {}.'.format( raw_ast[0])) return (raw_ast[0], *clauses) def _process_expr(self): self._num_vars = self._expr.degree ast = self._expr.to_cnf().to_ast() ast = LogicExpressionOracle._normalize_literal_indices( ast, self._expr.usupport) if self._optimization == 'off': self._nf = CNF(ast, num_vars=self._num_vars) else: # self._optimization == 'espresso': expr_dnf = self._expr.to_dnf() if expr_dnf.is_zero() or expr_dnf.is_one(): self._nf = CNF(('const', 0 if expr_dnf.is_zero() else 1), num_vars=self._num_vars) else: expr_dnf_m = espresso_exprs(expr_dnf)[0] expr_dnf_m_ast = LogicExpressionOracle._normalize_literal_indices( expr_dnf_m.to_ast(), expr_dnf_m.usupport) if isinstance(expr_dnf_m, AndOp): self._nf = CNF(expr_dnf_m_ast, num_vars=self._num_vars) elif isinstance(expr_dnf_m, OrOp): self._nf = DNF(expr_dnf_m_ast, num_vars=self._num_vars) else: raise AquaError( 'Unexpected espresso optimization result expr: {}'. format(expr_dnf_m)) @property def variable_register(self): return self._variable_register @property def ancillary_register(self): return self._ancillary_register @property def output_register(self): return self._output_register def construct_circuit(self): if self._circuit is None: if self._nf is not None: self._circuit = self._nf.construct_circuit( mct_mode=self._mct_mode) self._variable_register = self._nf.variable_register self._output_register = self._nf.output_register self._ancillary_register = self._nf.ancillary_register else: self._variable_register = QuantumRegister(self._num_vars, name='v') self._output_register = QuantumRegister(1, name='o') self._ancillary_register = None self._circuit = QuantumCircuit(self._variable_register, self._output_register) return self._circuit def evaluate_classically(self, measurement): assignment = [ (var + 1) * (int(tf) * 2 - 1) for tf, var in zip(measurement[::-1], range(len(measurement))) ] if self._expr.is_zero(): found = False elif self._expr.is_one(): found = True else: prime_implicants = self._expr.complete_sum() if isinstance(prime_implicants, AndOp): prime_implicants_ast = LogicExpressionOracle._normalize_literal_indices( prime_implicants.to_ast(), prime_implicants.usupport) sols = [[l[1] for l in prime_implicants_ast[1:]]] elif isinstance(prime_implicants, OrOp): expr_complete_sum = self._expr.complete_sum() complete_sum_ast = LogicExpressionOracle._normalize_literal_indices( expr_complete_sum.to_ast(), expr_complete_sum.usupport) sols = [[l[1] for l in c[1:]] for c in complete_sum_ast[1:]] else: raise AquaError( 'Unexpected solution: {}'.format(prime_implicants)) found = assignment in sols return found, assignment