Skip to content

Conversation

@Seva-Vaskin
Copy link
Collaborator

This PR adds two new features to cirbo:

  1. AIG Parser - Support for reading AIGER format files (.aag and .aig)
  2. RemoveConstantGates - A new circuit simplification transformer

@Seva-Vaskin Seva-Vaskin requested a review from spefk December 3, 2025 22:08
Copy link
Collaborator

@spefk spefk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Оставил разных комментариев (и минорных, и важных).

В целом, лучше приносить такие большие ПРы частями, потому что 4к кода сложно править и ревьюить (и вообще, по опыту количетсво строк в ПРе как минимум квадратично влияет на время проверки!). В этом ПРе как раз 2 несвязанные части есть, парсер и алгоритм минимизации, которые можно было бы разделить чтобы одно за другое не цеплялось.

"pebble >= 5.0.7",
"graphviz >= 0.20.3",
"sortedcontainers (>=2.4.0,<3.0.0)",
"notebook (>=7.4.7,<8.0.0)",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это лишняя зависимость для либы т.к. непосредственно не используется (и вообще, скорее однозначно пользовательская) -- нужно убрать и откатить poetry.lock на предыдущую версию (т.е. не update, а честный revert по истории)

И в целом, добавление/обновление зависимостей всегда лучше приносить отдельным ПРом, т.к. так проще отслеживать проблемы.

Comment on lines +219 to +251
@staticmethod
def from_aig_string(string: str) -> "Circuit":
"""
Initialize the circuit from an AIG format string.
Only ASCII AIG format (.aag) is supported for string input. Only combinational
circuits (without latches) are supported.
:param string: string containing AIG data in ASCII format.
:return: parsed Circuit object.
"""
from cirbo.core.parser.aig import AIGParser

parser = AIGParser()
return parser.parse_string(string)

@staticmethod
def from_aig_bytes(data: bytes) -> "Circuit":
"""
Initialize the circuit from AIG format bytes.
Supports both ASCII (.aag) and binary (.aig) AIGER formats. Only combinational
circuits (without latches) are supported.
:param data: bytes containing AIG data.
:return: parsed Circuit object.
"""
from cirbo.core.parser.aig import AIGParser

parser = AIGParser()
return parser.parse_bytes(data)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Минорно: правда ли на уровне парсера (и схемы соотв.) должны быть разные методы parse_bytes и parse_string, если это можно простой проверкой isinstance вывести динамически? Отдельный parse_file нужен чтобы отличить строку с путём от строки с данными

__all__ = ['AIGParser']


class AIGParseError(Exception):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно отнаследовать от CirboError

pass


class AIGParser:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не нравится, что эта штука не наследуется от AbstractParser. Получаем две отдельные абстракции, из-за чего целостность теряется. Давай отнаследуем её и как базовый сценарий поддержим работу со строковым входом?

"""
path = pathlib.Path(file_path)
if path.suffix == '.aag':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Минорно: чуть чище будет с endswith

nonlocal _new_circuit, _label_remap, _gate_types

# remap operands to their simplified labels
remapped_operands = tuple(_get_remapped_label(op) for op in _gate.operands)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Минорно: тот относительно редкий случай, когда можно tuple(map(_get_remapped_label, _gate.operands))

Comment on lines +33 to +34
def _get_const_value(gate_type: gate.GateType) -> int:
return 1 if gate_type == gate.ALWAYS_TRUE else 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Немного течет интерфейс -- конст. гейты задаются в _CONST_GATES, а здесь проверяется только gate.ALWAYS_TRUE и else. Чище будет честно проверять оба if == gate.ALWAYS_TRUE и if == gate.ALWAYS_TRUE, и бросать ошибку иначе.

Как вариант, можно заменить _CONST_GATES на дикт где ключи -- типы, а значения -- константные значения этих типов гейтов.

Сами значения правильнее вычислять через вызов .operator (т.е. ALWAYS_TRUE.operator())

const_operand_infos.append((idx, _get_const_value(op_type)))

# simplify if we have constant operands
if const_operand_infos and current_type not in (gate.INPUT, *_CONST_GATES):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На каждой итерации создается новый тапл в (gate.INPUT, *_CONST_GATES), что дорого.

return new_type, new_operands


class RemoveConstantGates(Transformer):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У нас аналогичное упрощение написано в Simplifier'e, и перетащено в cirbo-search сейчас. Я точно не помно, но там вроде поддержано всякое, в т.ч. не только бинарные гейты. Если интересно, можно посмотреть как оно реализовано и поспрашивать Вику, если будет необходимость: https://github.com/SPbSAT/cirbo-search/blob/main/src/minimization/low_effort/constant_gate_reducer.hpp

В целом, в будущем планируем cirbo-search подсоединить в cirbo биндингами, и вызывать упрощения из него, поэтому доработки в рамках упрощений cirbo скорее ограниченно актуальны.

Comment on lines +44 to +51
if original_gate.gate_type == gate.IFF:
# IFF(const) = const
new_type = operand_gate_type
elif original_gate.gate_type == gate.NOT:
# NOT(ALWAYS_TRUE) = ALWAYS_FALSE, NOT(ALWAYS_FALSE) = ALWAYS_TRUE
new_type = gate.ALWAYS_FALSE if const_val == 1 else gate.ALWAYS_TRUE
else:
raise ValueError(f"Unsupported unary gate type: {original_gate.gate_type}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь тоже идет хардкод конкретных видов гейтов. Имхо, правильнее вызывать operator в нужных местах и довериться полиморфизму.

@spefk spefk changed the title Add AIG Parser and RemoveConstantGates Simplification feat all: add AIG parser and RemoveConstantGates minimization Dec 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants