1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
mod constraints;
mod degree;
mod operation;
mod trace;
mod value;

pub use self::constraints::{ConstraintDomain, ConstraintError, ConstraintRoot, Constraints};
pub use self::degree::IntegrityConstraintDegree;
pub use self::operation::Operation;
pub use self::trace::TraceAccess;
pub use self::value::{PeriodicColumnAccess, PublicInputAccess, Value};

pub use air_parser::{
    ast::{
        AccessType, Boundary, Identifier, PeriodicColumn, PublicInput, QualifiedIdentifier,
        TraceSegmentId,
    },
    Symbol,
};

/// The default segment against which a constraint is applied is the main trace segment.
pub const DEFAULT_SEGMENT: TraceSegmentId = 0;
/// The auxiliary trace segment.
pub const AUX_SEGMENT: TraceSegmentId = 1;
/// The offset of the "current" row during constraint evaluation.
pub const CURRENT_ROW: usize = 0;
/// The minimum cycle length of a periodic column
pub const MIN_CYCLE_LENGTH: usize = 2;

use std::collections::BTreeMap;

use miden_diagnostics::{SourceSpan, Spanned};

use crate::graph::AlgebraicGraph;

/// The intermediate representation of a complete AirScript program
///
/// This structure is produced from an [air_parser::ast::Program] that has
/// been through semantic analysis, constant propagation, and inlining. It
/// is equivalent to an [air_parser::ast::Program], except that it has been
/// translated into an algebraic graph representation, on which further analysis,
/// optimization, and code generation are performed.
#[derive(Debug, Spanned)]
pub struct Air {
    /// The name of the [air_parser::ast::Program] from which this IR was derived
    #[span]
    pub name: Identifier,
    /// The widths (number of columns) of each segment of the trace, in segment order (i.e. the
    /// index in this vector matches the index of the segment in the program).
    pub trace_segment_widths: Vec<u16>,
    /// The periodic columns referenced by this program.
    ///
    /// These are taken straight from the [air_parser::ast::Program] without modification.
    pub periodic_columns: BTreeMap<QualifiedIdentifier, PeriodicColumn>,
    /// The public inputs referenced by this program.
    ///
    /// These are taken straight from the [air_parser::ast::Program] without modification.
    pub public_inputs: BTreeMap<Identifier, PublicInput>,
    /// The total number of elements in the random values array
    pub num_random_values: u16,
    /// The constraints enforced by this program, in their algebraic graph representation.
    pub constraints: Constraints,
}
impl Default for Air {
    fn default() -> Self {
        Self::new(Identifier::new(
            SourceSpan::UNKNOWN,
            Symbol::intern("unnamed"),
        ))
    }
}
impl Air {
    /// Create a new, empty [Air] container
    ///
    /// An empty [Air] is meaningless until it has been populated with
    /// constraints and associated metadata. This is typically done by converting
    /// an [air_parser::ast::Program] to this struct using the [crate::passes::AstToAir]
    /// translation pass.
    pub fn new(name: Identifier) -> Self {
        Self {
            name,
            trace_segment_widths: vec![],
            periodic_columns: Default::default(),
            public_inputs: Default::default(),
            num_random_values: 0,
            constraints: Default::default(),
        }
    }

    /// Returns the name of the [air_parser::ast::Program] this [Air] was derived from, as a `str`
    #[inline]
    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    pub fn public_inputs(&self) -> impl Iterator<Item = &PublicInput> + '_ {
        self.public_inputs.values()
    }

    pub fn periodic_columns(&self) -> impl Iterator<Item = &PeriodicColumn> + '_ {
        self.periodic_columns.values()
    }

    /// Return the number of boundary constraints
    pub fn num_boundary_constraints(&self, trace_segment: TraceSegmentId) -> usize {
        self.constraints.num_boundary_constraints(trace_segment)
    }

    /// Return the set of [ConstraintRoot] corresponding to the boundary constraints
    pub fn boundary_constraints(&self, trace_segment: TraceSegmentId) -> &[ConstraintRoot] {
        self.constraints.boundary_constraints(trace_segment)
    }

    /// Return the set of [ConstraintRoot] corresponding to the integrity constraints
    pub fn integrity_constraints(&self, trace_segment: TraceSegmentId) -> &[ConstraintRoot] {
        self.constraints.integrity_constraints(trace_segment)
    }

    /// Return the set of [IntegrityConstraintDegree] corresponding to each integrity constraint
    pub fn integrity_constraint_degrees(
        &self,
        trace_segment: TraceSegmentId,
    ) -> Vec<IntegrityConstraintDegree> {
        self.constraints.integrity_constraint_degrees(trace_segment)
    }

    /// Return an [Iterator] over the validity constraints for the given trace segment
    pub fn validity_constraints(
        &self,
        trace_segment: TraceSegmentId,
    ) -> impl Iterator<Item = &ConstraintRoot> + '_ {
        self.constraints
            .integrity_constraints(trace_segment)
            .iter()
            .filter(|constraint| matches!(constraint.domain(), ConstraintDomain::EveryRow))
    }

    /// Return an [Iterator] over the transition constraints for the given trace segment
    pub fn transition_constraints(
        &self,
        trace_segment: TraceSegmentId,
    ) -> impl Iterator<Item = &ConstraintRoot> + '_ {
        self.constraints
            .integrity_constraints(trace_segment)
            .iter()
            .filter(|constraint| matches!(constraint.domain(), ConstraintDomain::EveryFrame(_)))
    }

    /// Return a reference to the raw [AlgebraicGraph] corresponding to the constraints
    #[inline]
    pub fn constraint_graph(&self) -> &AlgebraicGraph {
        self.constraints.graph()
    }

    /// Return a mutable reference to the raw [AlgebraicGraph] corresponding to the constraints
    #[inline]
    pub fn constraint_graph_mut(&mut self) -> &mut AlgebraicGraph {
        self.constraints.graph_mut()
    }
}