11#pragma once
22
3- #include < algorithm>
4- #include < map>
53#include < memory>
6- #include < string>
4+ #include < ranges>
5+ #include < sstream>
76#include < type_traits>
7+ #include < unordered_map>
8+ #include < vector>
89
910#include " src/algo.hpp"
10- #include " src/common/csat_types.hpp"
1111#include " src/simplification/transformer_base.hpp"
12- #include " src/structures/circuit/gate_info.hpp"
13- #include " src/structures/circuit/icircuit.hpp"
1412#include " src/utility/converters.hpp"
15- #include " src/utility/encoder.hpp"
16- #include " src/utility/logger.hpp"
1713
1814namespace csat ::simplification
1915{
@@ -23,114 +19,186 @@ namespace csat::simplification
2319 * Duplicates are gates with the same operands and operator
2420 *
2521 * Note that this algorithm requires RedundantGatesCleaner to be applied right before.
22+ * Note that this algorithm will not reduce duplicate operands of gate, but will account
23+ * them "as one operand" during duplicates search. To reduce such gates one can use
24+ * separate `DuplicateOperandsCleaner` strategy.
2625 *
2726 * @tparam CircuitT
2827 */
29- template <class CircuitT , typename = std::enable_if_t <std::is_base_of_v<ICircuit, CircuitT>> >
28+ template <class CircuitT , typename = std::enable_if_t <std::is_base_of_v<ICircuit, CircuitT> > >
3029class DuplicateGatesCleaner_ : public ITransformer <CircuitT>
3130{
3231 csat::Logger logger{" DuplicateGatesCleaner" };
3332
3433 public:
3534 CircuitAndEncoder<CircuitT, std::string> transform (
3635 std::unique_ptr<CircuitT> circuit,
37- std::unique_ptr<GateEncoder<std::string> > encoder)
36+ std::unique_ptr<GateEncoder> encoder)
3837 {
3938 logger.debug (" =========================================================================================" );
4039 logger.debug (" START DuplicateGatesCleaner" );
4140
42- logger.debug (" Top sort" );
41+ GateEncoder new_encoder{};
42+
43+ logger.debug (" Performing top sort" );
44+ // Topsort, from inputs to outputs.
4345 csat::GateIdContainer gateSorting (algo::TopSortAlgorithm<algo::DFSTopSort>::sorting (*circuit));
4446 std::reverse (gateSorting.begin (), gateSorting.end ());
4547
4648 logger.debug (" Building mask to delete gates and filling map -- old_to_new_gateId" );
4749 // 0 -- if gate is a duplicate, 1 -- otherwise
48- BoolVector safe_mask (circuit->getNumberOfGates (), 1 );
49- // maps encoded (`operator_operand1_operand2...`) gate to new gate id
50- GateEncoder<std::string> auxiliary_names_encoder{};
51- // surjection of old gate ids to new gate ids
52- std::map<GateId, GateId> old_to_new_gateId{};
53- // bijection of old gate ids to new gate ids
54- GateEncoder<GateId> new_encoder{};
55- std::string encoded_name;
56-
50+ BoolVector safe_mask (circuit->getNumberOfGates (), true );
51+ // `auxiliary_encoder` helps to deduplicate gates by mapping same auxiliary name to one index.
52+ GateEncoder auxiliary_encoder{};
53+ // maps original gate ID to auxiliary ID, gotten from `auxiliary_encoder`.
54+ std::unordered_map<GateId, GateId> gate_id_to_auxiliary_id{};
55+
56+ logger.debug (" Building mask to delete gates and filling map -- gate_id_to_auxiliary_id" );
57+ std::string auxiliary_name;
5758 for (GateId gateId : gateSorting)
5859 {
59- encoded_name = get_gate_auxiliary_name_ (
60- gateId, circuit->getGateType (gateId), circuit->getGateOperands (gateId), old_to_new_gateId);
61- logger.debug (" Gate number " , gateId, " . Its encoded_name is " , encoded_name);
60+ logger.debug (" Processing gate " , gateId);
61+ auxiliary_name.clear ();
62+ auxiliary_name = formatGateAuxiliaryName_ (
63+ gateId, circuit->getGateType (gateId), circuit->getGateOperands (gateId), gate_id_to_auxiliary_id);
64+ logger.debug (" Auxiliary name for gate " , gateId, " is " , auxiliary_name);
6265
63- if (auxiliary_names_encoder .keyExists (encoded_name ))
66+ if (auxiliary_encoder .keyExists (auxiliary_name ))
6467 {
6568 logger.debug (" Gate number " , gateId, " is a Duplicate and will be removed." );
66- safe_mask.at (gateId) = 0 ;
69+ safe_mask.at (gateId) = false ;
6770 }
6871 else
6972 {
70- new_encoder.encodeGate (gateId);
73+ logger.debug (
74+ " Gate number " , gateId, " is either unique, or first of found duplicated, and will be saved." );
75+ new_encoder.encodeGate (encoder->decodeGate (gateId));
7176 }
7277
73- old_to_new_gateId [gateId] = auxiliary_names_encoder .encodeGate (encoded_name );
78+ gate_id_to_auxiliary_id [gateId] = auxiliary_encoder .encodeGate (auxiliary_name );
7479 }
7580
7681 logger.debug (" Building new circuit" );
77- GateInfoContainer gate_info (auxiliary_names_encoder .size ());
82+ GateInfoContainer gate_info (auxiliary_encoder .size ());
7883 for (GateId gateId = 0 ; gateId < circuit->getNumberOfGates (); ++gateId)
7984 {
80- if (safe_mask.at (gateId) != 0 )
85+ if (safe_mask.at (gateId))
8186 {
8287 logger.debug (
8388 " New Gate " ,
84- old_to_new_gateId .at (gateId),
89+ gate_id_to_auxiliary_id .at (gateId),
8590 " ; Type: " ,
8691 utils::gateTypeToString (circuit->getGateType (gateId)),
87- " ; Operands: " );
92+ " ; Operands: " ,
93+ formatOperandsString_ (circuit->getGateOperands (gateId), gate_id_to_auxiliary_id).str ());
8894
8995 GateIdContainer masked_operands_{};
9096 for (GateId operand : circuit->getGateOperands (gateId))
9197 {
92- masked_operands_.push_back (old_to_new_gateId.at (operand));
93- logger.debug (old_to_new_gateId.at (operand));
98+ masked_operands_.push_back (gate_id_to_auxiliary_id.at (operand));
9499 }
95- gate_info.at (old_to_new_gateId .at (gateId)) = {circuit->getGateType (gateId), masked_operands_};
100+ gate_info.at (gate_id_to_auxiliary_id .at (gateId)) = {circuit->getGateType (gateId), masked_operands_};
96101 }
97102 }
98103
99104 GateIdContainer new_output_gates{};
100105 new_output_gates.reserve (circuit->getOutputGates ().size ());
101106 for (GateId output_gate : circuit->getOutputGates ())
102107 {
103- new_output_gates.push_back (old_to_new_gateId .at (output_gate));
108+ new_output_gates.push_back (gate_id_to_auxiliary_id .at (output_gate));
104109 }
105110
106111 logger.debug (" END DuplicateGatesCleaner" );
107112 logger.debug (" =========================================================================================" );
108- return {
109- std::make_unique<CircuitT>(gate_info, new_output_gates), utils::mergeGateEncoders (*encoder, new_encoder)};
113+ return {std::make_unique<CircuitT>(gate_info, new_output_gates), std::make_unique<GateEncoder>(new_encoder)};
110114 };
111115
112116 private:
113- std::string get_gate_auxiliary_name_ (
114- GateId idx,
115- GateType type,
117+ /* *
118+ * Formats new auxiliary name for the gate based on its attributes (type, operands).
119+ * Such name may be used to determine literal duplicates in the circuit.
120+ *
121+ * @param gateId -- original ID of gate.
122+ * @param gateType -- type of gate.
123+ * @param operands -- gate's operand IDs.
124+ * @param encoder -- mapping of circuit gates' original IDs to new names. Must already contain
125+ * value for each operand of provided gate. It is needed to detect dependant duplicates
126+ * in only one circuit iteration.
127+ * @return auxiliary name of gate.
128+ */
129+ std::string formatGateAuxiliaryName_ (
130+ GateId gateId,
131+ GateType gateType,
116132 GateIdContainer const & operands,
117- std::map <GateId, GateId> const & encoder )
133+ std::unordered_map <GateId, GateId> const & deduplicator )
118134 {
119- std::string encoded_name;
120- encoded_name = std::to_string (static_cast <int >(type));
135+ std::stringstream auxiliary_name;
136+ auxiliary_name << std::to_string (static_cast <uint8_t >(gateType));
137+
138+ // All Input gates are unique, but we can't differentiate them
139+ // by their operands (since there are none), so we simply add
140+ // their current (unique) index to auxiliary name and return.
141+ if (gateType == GateType::INPUT)
142+ {
143+ auxiliary_name << ' _' + std::to_string (gateId);
144+ return auxiliary_name.str ();
145+ }
121146
122- if (type == GateType::INPUT)
147+ // Preparing set of operands, by accounting already found duplicates.
148+ GateIdContainer prepared_operands{};
149+ prepared_operands.reserve (operands.size ());
150+ std::transform (
151+ operands.begin (),
152+ operands.end (),
153+ std::back_inserter (prepared_operands),
154+ [&deduplicator](GateId operand) { return deduplicator.at (operand); });
155+
156+ // For gates defined by symmetrical function operands are sorted during
157+ // circuit construction (see GateInfo), but then some of their operands
158+ // may be decided to be duplicates, so we resort them so such gates with
159+ // same set of operands will have same auxiliary name and will be detected
160+ // as duplicates.
161+ if (utils::symmetricOperatorQ (gateType))
123162 {
124- encoded_name += ' _' + std::to_string (idx);
163+ std::sort (prepared_operands.begin (), prepared_operands.end ());
164+ // Since several operands of same gate may be decided to be duplicates
165+ // we can have here something like `AND(X, X, Y)`, which is equivalent
166+ // to the `AND(X, Y)`, hence to allow such duplicated to be found we
167+ // also clean such duplicates.
168+ if (utils::reducibleMultipleOperandsQ (gateType))
169+ {
170+ // Note: `unique` requires container to be sorted.
171+ auto last = std::unique (prepared_operands.begin (), prepared_operands.end ());
172+ prepared_operands.erase (last, prepared_operands.end ());
173+ }
125174 }
126175
127- for (GateId const operand : operands)
176+ // Adds names of gate's operands to its auxiliary name.
177+ for (GateId operand : prepared_operands)
128178 {
129- encoded_name += ' _' + std::to_string (encoder. at ( operand) );
179+ auxiliary_name << ' _' + std::to_string (operand);
130180 }
131181
132- return encoded_name ;
182+ return auxiliary_name. str () ;
133183 };
184+
185+ std::stringstream formatOperandsString_ (
186+ GateIdContainer const & operands,
187+ std::unordered_map<GateId, GateId> const & encoder)
188+ {
189+ std::stringstream ss;
190+ if (operands.empty ())
191+ {
192+ return ss;
193+ }
194+
195+ ss << encoder.at (operands[0 ]);
196+ for (auto ptr = operands.begin () + 1 ; ptr != operands.end (); ++ptr)
197+ {
198+ ss << ' ,' << encoder.at (*ptr);
199+ }
200+ return ss;
201+ }
134202};
135203
136- } // namespace csat::simplification
204+ } // namespace csat::simplification
0 commit comments