Skip to content

feat: Circuits definition in python using magic methods on ModuloCircuitElement #359

@feltroidprime

Description

@feltroidprime

We want to do :

c = ModuloCircuit()
a = c.write_element(0)
b = c.write_element(1)

d = a + b # Instead of d = circuit.add(a, b)

circuit.compile_circuit()

We can achieve it by adding the magic methods to the ModuloCircuitElement class and by adding the circuit they are tied to at initialization:

@dataclass(slots=True)
class ModuloCircuitElement:
    """
    Represents an element within a modulo circuit with its associated offset.

    Attributes:
        emulated_felt (PyFelt): The emulated field element in the modulo circuit
        offset (int): The offset in the values segment within the modulo circuit where the element is stored.
    """

    emulated_felt: PyFelt
    offset: int
    circuit: ModuloCircuit

    __repr__ = lambda self: f"ModuloCircuitElement({hex(self.value)}, {self.offset})"

    @property
    def value(self) -> int:
        return self.emulated_felt.value

    @property
    def p(self) -> int:
        return self.emulated_felt.p

    @property
    def felt(self) -> PyFelt:
        return self.emulated_felt
    
    def __add__(self, other: ModuloCircuitElement) -> ModuloCircuitElement:
        return self.circuit.add(self, other)
    
    def __neg__(self) -> ModuloCircuitElement:
        return self.circuit.neg(self)
    
    def __sub__(self, other: ModuloCircuitElement) -> ModuloCircuitElement:
        return self.circuit.sub(self, other)
    
    def __mul__(self, other: ModuloCircuitElement) -> ModuloCircuitElement:
        return self.circuit.mul(self, other)
    
    def __truediv__(self, other: ModuloCircuitElement) -> ModuloCircuitElement:
        return self.circuit.div(self, other) 

And then changing associated methods in ModuloCircuit :

    def write_element(
        self,
        elmt: PyFelt | int,
        write_source: WriteOps = WriteOps.INPUT,
        instruction: ModuloCircuitInstruction | None = None,
    ) -> ModuloCircuitElement:
        """
        Register an emulated field element to the circuit given its value and the write source.
        Returns a ModuloCircuitElement representing the written element with its offset as identifier.
        """
        assert isinstance(elmt, PyFelt) or isinstance(
            elmt, int
        ), f"Expected PyFelt or int, got {type(elmt)}"
        if isinstance(elmt, int):
            elmt = self.field(elmt)
        value_offset = self.values_segment.write_to_segment(
            ValueSegmentItem(
                elmt,
                write_source,
                instruction,
            )
        )
        res = ModuloCircuitElement(elmt, value_offset, circuit=self)
        return res

We add self.uuid = uuid.uuid4() on the init method the ModuloCircuit, and then on the basic add sub mul div methods of ModuloCircuit, we add the check

assert a.circuit.uuid == b.circuit.uuid, "Cannot multiply elements from different circuits"

However this leads to python cyclic imports and requires bigger refactor of the python package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementEnhancement of the code, not introducing new features.help-wantedWe need some extra helping hands or expertise in order to resolve this!python

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions