1use crate::internal::*;
2use crate::ops::array::MultiBroadcastTo;
3
4pub fn cast(to: DatumType) -> Cast {
5 Cast { to }
6}
7
8pub fn wire_cast(
9 prefix: impl AsRef<str>,
10 target: &mut TypedModel,
11 inputs: &[OutletId],
12 operating_datum_type: DatumType,
13) -> TractResult<TVec<OutletId>> {
14 let prefix = prefix.as_ref();
15 let mut wires = tvec!();
16 for mut wire in inputs.iter().copied() {
17 if target.outlet_fact(wire)?.datum_type != operating_datum_type {
18 wire = target.wire_node(
19 target.unique_name(format!("{prefix}.cast")),
20 crate::ops::cast::cast(operating_datum_type),
21 &[wire],
22 )?[0];
23 }
24 wires.push(wire);
25 }
26 Ok(wires)
27}
28
29#[derive(Debug, Clone, new, Hash, PartialEq, Eq)]
30pub struct Cast {
31 pub to: DatumType,
32}
33
34impl Op for Cast {
35 fn name(&self) -> StaticName {
36 "Cast".into()
37 }
38
39 op_as_typed_op!();
40}
41
42impl EvalOp for Cast {
43 op_out_of_plan!();
44
45 fn eval(&self, ctx: &EvalContext, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
46 let input = args_1!(inputs);
47 if input.datum_type() == self.to {
48 Ok(tvec!(input))
49 } else if input.datum_type() == TDim::datum_type() {
50 let mut tmp = Tensor::zero_dt(i64::datum_type(), input.shape())?;
51 let input_plain = input.try_as_plain_ram()?;
52 let mut tmp_plain = tmp.try_as_plain_ram_mut()?;
53 for (dim, i) in tract_itertools::izip!(
54 input_plain.as_slice::<TDim>()?,
55 tmp_plain.as_slice_mut::<i64>()?
56 ) {
57 *i = dim.eval(ctx.symbols).to_i64()?
58 }
59 Ok(tvec!(tmp.cast_to_dt(self.to)?.into_owned().into_tvalue()))
60 } else {
61 Ok(tvec!(input.cast_to_dt(self.to)?.into_owned().into_tvalue()))
62 }
63 }
64}
65
66impl TypedOp for Cast {
67 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
68 let mut fact = self.to.fact(inputs[0].shape.clone());
69 fact.uniform_tdim = inputs[0].uniform_tdim.clone();
70 if fact.uniform_tdim.is_none() {
71 for t in
75 [inputs[0].uniform.as_deref(), inputs[0].konst.as_deref()].into_iter().flatten()
76 {
77 if t.datum_type() == TDim::datum_type() && t.volume() == 1 {
78 fact.uniform_tdim = Some(
79 t.try_as_plain_ram()?.to_scalar::<TDim>()?.eval(&SymbolValues::default()),
80 );
81 break;
82 }
83 }
84 }
85 if let Some(u) = &inputs[0].uniform
86 && let Ok(cast_u) = u.cast_to_dt(self.to)
87 {
88 fact.uniform = Some(std::sync::Arc::new(cast_u.into_owned()));
89 }
90 Ok(tvec!(fact))
91 }
92
93 fn input_roi(
94 &self,
95 model: &TypedModel,
96 node: &TypedNode,
97 ) -> TractResult<Option<TVec<Option<TDim>>>> {
98 crate::optim::propagate_roi::bubble_roi(model, node)
99 }
100
101 fn declutter(
102 &self,
103 model: &TypedModel,
104 node: &TypedNode,
105 ) -> TractResult<Option<TypedModelPatch>> {
106 if model.outlet_fact(node.inputs[0])?.datum_type == self.to {
107 return TypedModelPatch::shunt_one_op(model, node);
108 }
109 if let Some(prec) = model.linear_prec(node.id)?
119 && (prec.op_is::<IntoShape>() || prec.op_is::<MultiBroadcastTo>())
120 {
121 let mut patch = TypedModelPatch::default();
122 let mut wire = tvec!(patch.tap_model(model, prec.inputs[0])?);
123 wire = patch.wire_node(&node.name, &node.op, &wire)?;
124 wire = patch.wire_node(&prec.name, &prec.op, &wire)?;
125 patch.shunt_outside(model, node.id.into(), wire[0])?;
126 return Ok(Some(patch));
127 }
128 Ok(None)
129 }
130
131 fn axes_mapping(
132 &self,
133 inputs: &[&TypedFact],
134 outputs: &[&TypedFact],
135 ) -> TractResult<AxesMapping> {
136 AxesMapping::natural(inputs, outputs)
137 }
138
139 fn change_axes(
140 &self,
141 model: &TypedModel,
142 node: &TypedNode,
143 _io: InOut,
144 change: &AxisOp,
145 ) -> TractResult<Option<AxisChangeConsequence>> {
146 Ok(Some(AxisChangeConsequence::new(model, node, None, change)))
147 }
148
149 fn slice(
150 &self,
151 patch: &mut TypedModelPatch,
152 _model: &TypedModel,
153 node: &TypedNode,
154 _prefix: &str,
155 inputs: &[OutletId],
156 _output_axis: usize,
157 _start: &TDim,
158 _end: &TDim,
159 ) -> TractResult<Option<TVec<OutletId>>> {
160 patch.wire_node(&node.name, &node.op, inputs).map(Some)
161 }
162
163 as_op!();
164}