1use std::ffi::c_void;
8use std::marker::PhantomData;
9use std::rc::Rc;
10
11use hullabaloo::set_family::SetFamily as HbSetFamily;
12use hullabaloo::types::{
13 Representation as RepresentationMarker, RepresentationKind, RowSet as HbRowSet,
14};
15use thiserror::Error;
16pub use hullabaloo::types::{Generator, Inequality};
17
18#[cfg(all(
19 not(feature = "gmprational"),
20 not(feature = "gmp"),
21 not(feature = "f64")
22))]
23compile_error!("cddlib-rs requires at least one backend feature: f64, gmp, or gmprational");
24
25mod backend;
26mod raw;
27
28pub use backend::{CddNumber, DefaultNumber};
29
30#[cfg(feature = "gmp")]
31pub use backend::CddFloat;
32
33#[cfg(feature = "gmprational")]
34pub use backend::CddRational;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum CddErrorCode {
38 DimensionTooLarge,
39 ImproperInputFormat,
40 NegativeMatrixSize,
41 EmptyVRepresentation,
42 EmptyHRepresentation,
43 EmptyRepresentation,
44 InputFileNotFound,
45 OutputFileNotOpen,
46 NoLpObjective,
47 NoRealNumberSupport,
48 NotAvailForH,
49 NotAvailForV,
50 CannotHandleLinearity,
51 RowIndexOutOfRange,
52 ColIndexOutOfRange,
53 LpCycling,
54 NumericallyInconsistent,
55 NoError,
56}
57
58impl CddErrorCode {
59 pub fn from_raw(raw: u32) -> Self {
60 match raw {
61 0 => Self::DimensionTooLarge,
62 1 => Self::ImproperInputFormat,
63 2 => Self::NegativeMatrixSize,
64 3 => Self::EmptyVRepresentation,
65 4 => Self::EmptyHRepresentation,
66 5 => Self::EmptyRepresentation,
67 6 => Self::InputFileNotFound,
68 7 => Self::OutputFileNotOpen,
69 8 => Self::NoLpObjective,
70 9 => Self::NoRealNumberSupport,
71 10 => Self::NotAvailForH,
72 11 => Self::NotAvailForV,
73 12 => Self::CannotHandleLinearity,
74 13 => Self::RowIndexOutOfRange,
75 14 => Self::ColIndexOutOfRange,
76 15 => Self::LpCycling,
77 16 => Self::NumericallyInconsistent,
78 17 => Self::NoError,
79 other => panic!("unknown dd_ErrorType value {other}"),
80 }
81 }
82
83 pub fn as_raw(self) -> u32 {
84 match self {
85 Self::DimensionTooLarge => 0,
86 Self::ImproperInputFormat => 1,
87 Self::NegativeMatrixSize => 2,
88 Self::EmptyVRepresentation => 3,
89 Self::EmptyHRepresentation => 4,
90 Self::EmptyRepresentation => 5,
91 Self::InputFileNotFound => 6,
92 Self::OutputFileNotOpen => 7,
93 Self::NoLpObjective => 8,
94 Self::NoRealNumberSupport => 9,
95 Self::NotAvailForH => 10,
96 Self::NotAvailForV => 11,
97 Self::CannotHandleLinearity => 12,
98 Self::RowIndexOutOfRange => 13,
99 Self::ColIndexOutOfRange => 14,
100 Self::LpCycling => 15,
101 Self::NumericallyInconsistent => 16,
102 Self::NoError => 17,
103 }
104 }
105
106 fn cddlib_name(self) -> &'static str {
107 match self {
108 Self::DimensionTooLarge => "dd_DimensionTooLarge",
109 Self::ImproperInputFormat => "dd_ImproperInputFormat",
110 Self::NegativeMatrixSize => "dd_NegativeMatrixSize",
111 Self::EmptyVRepresentation => "dd_EmptyVrepresentation",
112 Self::EmptyHRepresentation => "dd_EmptyHrepresentation",
113 Self::EmptyRepresentation => "dd_EmptyRepresentation",
114 Self::InputFileNotFound => "dd_IFileNotFound",
115 Self::OutputFileNotOpen => "dd_OFileNotOpen",
116 Self::NoLpObjective => "dd_NoLPObjective",
117 Self::NoRealNumberSupport => "dd_NoRealNumberSupport",
118 Self::NotAvailForH => "dd_NotAvailForH",
119 Self::NotAvailForV => "dd_NotAvailForV",
120 Self::CannotHandleLinearity => "dd_CannotHandleLinearity",
121 Self::RowIndexOutOfRange => "dd_RowIndexOutOfRange",
122 Self::ColIndexOutOfRange => "dd_ColIndexOutOfRange",
123 Self::LpCycling => "dd_LPCycling",
124 Self::NumericallyInconsistent => "dd_NumericallyInconsistent",
125 Self::NoError => "dd_NoError",
126 }
127 }
128}
129
130impl std::fmt::Display for CddErrorCode {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 write!(f, "{} (code={})", self.cddlib_name(), self.as_raw())
133 }
134}
135
136#[derive(Debug, Error)]
137pub enum CddError {
138 #[error("cddlib returned {0}")]
139 Cdd(CddErrorCode),
140 #[error("cddlib returned a null pointer")]
141 NullPointer,
142 #[error("LP error")]
143 LpError,
144 #[error("LP did not have an optimal solution (status={0:?})")]
145 LpStatus(LpStatus),
146 #[error("cddlib operation returned failure")]
147 OpFailed,
148}
149
150#[derive(Debug, Error)]
151pub enum CddWrapperError {
152 #[error(transparent)]
153 Cdd(#[from] CddError),
154 #[error("invalid matrix dimensions (rows={rows}, cols={cols})")]
155 InvalidMatrix { rows: usize, cols: usize },
156}
157
158pub type CddResult<T> = std::result::Result<T, CddWrapperError>;
159
160fn representation_to_raw(repr: RepresentationKind) -> u32 {
161 match repr {
162 RepresentationKind::Inequality => 1,
163 RepresentationKind::Generator => 2,
164 }
165}
166
167fn representation_from_raw(raw: u32) -> RepresentationKind {
168 match raw {
169 1 => RepresentationKind::Inequality,
170 2 => RepresentationKind::Generator,
171 other => panic!("unknown dd_RepresentationType value {other}"),
172 }
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum NumberType {
177 Real,
178 Rational,
179}
180
181impl NumberType {
182 fn to_raw(self) -> u32 {
183 match self {
184 NumberType::Real => 1,
185 NumberType::Rational => 2,
186 }
187 }
188
189 fn from_raw(raw: u32) -> Self {
190 match raw {
191 1 => NumberType::Real,
192 2 => NumberType::Rational,
193 other => panic!("unknown dd_NumberType value {other}"),
194 }
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub enum LpObjective {
200 None,
201 Maximize,
202 Minimize,
203}
204
205impl LpObjective {
206 fn to_raw(self) -> u32 {
207 match self {
208 LpObjective::None => 0,
209 LpObjective::Maximize => 1,
210 LpObjective::Minimize => 2,
211 }
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum LpStatus {
217 Undecided,
218 Optimal,
219 Inconsistent,
220 DualInconsistent,
221 StructuralInconsistent,
222 StructuralDualInconsistent,
223 Unbounded,
224 DualUnbounded,
225}
226
227impl LpStatus {
228 fn from_raw(raw: u32) -> Self {
229 match raw {
230 0 => LpStatus::Undecided,
231 1 => LpStatus::Optimal,
232 2 => LpStatus::Inconsistent,
233 3 => LpStatus::DualInconsistent,
234 4 => LpStatus::StructuralInconsistent,
235 5 => LpStatus::StructuralDualInconsistent,
236 6 => LpStatus::Unbounded,
237 7 => LpStatus::DualUnbounded,
238 other => panic!("unknown dd_LPStatusType value {other}"),
239 }
240 }
241}
242
243#[derive(Debug)]
244pub struct Matrix<N: CddNumber = DefaultNumber, R: RepresentationMarker = Inequality> {
245 raw: raw::MatrixData<N>,
246 _no_send_sync: PhantomData<Rc<()>>,
247 _repr: PhantomData<R>,
248}
249
250#[derive(Debug)]
251pub struct CanonicalForm<N: CddNumber = DefaultNumber, R: RepresentationMarker = Inequality> {
252 pub matrix: Matrix<N, R>,
253 pub implicit_linearity: Vec<usize>,
254 pub redundant_rows: Vec<usize>,
255 pub positions: Vec<isize>,
256}
257
258impl<N: CddNumber, R: RepresentationMarker> Matrix<N, R> {
259 pub fn new(rows: usize, cols: usize, num_type: NumberType) -> CddResult<Self> {
260 Ok(Matrix {
261 raw: raw::MatrixData::new(rows, cols, R::KIND, num_type)?,
262 _no_send_sync: PhantomData,
263 _repr: PhantomData,
264 })
265 }
266
267 pub fn rows(&self) -> usize {
268 self.raw.rows()
269 }
270
271 pub fn cols(&self) -> usize {
272 self.raw.cols()
273 }
274
275 pub fn representation(&self) -> RepresentationKind {
276 R::KIND
277 }
278
279 pub fn number_type(&self) -> NumberType {
280 self.raw.number_type()
281 }
282
283 pub fn as_raw(&self) -> *mut c_void {
284 self.raw.as_raw()
285 }
286
287 pub unsafe fn from_raw(ptr: *mut c_void) -> Self {
293 Matrix {
294 raw: raw::MatrixData::from_raw(ptr),
295 _no_send_sync: PhantomData,
296 _repr: PhantomData,
297 }
298 }
299
300 pub fn clone_cdd(&self) -> CddResult<Self> {
301 Ok(Self {
302 raw: self.raw.clone_cdd()?,
303 _no_send_sync: PhantomData,
304 _repr: PhantomData,
305 })
306 }
307
308 pub fn set(&mut self, row: usize, col: usize, value: &N) {
309 self.raw.set_mytype(row, col, value);
310 }
311
312 pub fn get(&self, row: usize, col: usize) -> N {
313 self.raw.get_mytype(row, col)
314 }
315
316 pub fn set_real(&mut self, row: usize, col: usize, value: f64) {
317 self.raw.set_real(row, col, value);
318 }
319
320 pub fn set_int(&mut self, row: usize, col: usize, value: std::os::raw::c_long) {
321 self.raw.set_int(row, col, value);
322 }
323
324 pub fn get_real(&self, row: usize, col: usize) -> f64 {
325 self.raw.get_real(row, col)
326 }
327
328 pub fn set_objective_real(&mut self, coeffs: &[f64]) {
329 self.raw.set_objective_real(coeffs);
330 }
331
332 pub fn append_rows_in_place(&mut self, rows: &Matrix<N, R>) -> CddResult<()> {
333 self.raw.append_rows_in_place(&rows.raw)
334 }
335
336 pub fn append_rows(&self, rows: &Matrix<N, R>) -> CddResult<Self> {
337 Ok(Self {
338 raw: self.raw.append_rows(&rows.raw)?,
339 _no_send_sync: PhantomData,
340 _repr: PhantomData,
341 })
342 }
343
344 pub fn remove_row(&mut self, row: usize) -> CddResult<()> {
345 self.raw.remove_row(row)
346 }
347
348 pub fn is_row_redundant(&self, row: usize) -> CddResult<bool> {
349 self.raw.is_row_redundant(row)
350 }
351
352 pub fn redundant_rows(&self) -> CddResult<Vec<usize>> {
353 self.raw.redundant_rows()
354 }
355
356 pub fn canonicalize(&self) -> CddResult<CanonicalForm<N, R>> {
357 let (canon_raw, implicit, redundant, positions) = self.raw.canonicalize()?;
358 let canon = Matrix {
359 raw: canon_raw,
360 _no_send_sync: PhantomData,
361 _repr: PhantomData,
362 };
363 Ok(CanonicalForm {
364 matrix: canon,
365 implicit_linearity: implicit,
366 redundant_rows: redundant,
367 positions,
368 })
369 }
370}
371
372impl<N: CddNumber> Matrix<N, Generator> {
373 pub fn set_generator_type(&mut self, row: usize, is_vertex: bool) {
374 self.raw.set_generator_type(row, is_vertex);
375 }
376
377 pub fn from_vertices<const D: usize>(vertices: &[[N; D]]) -> CddResult<Self> {
378 if vertices.is_empty() {
379 return Err(CddWrapperError::InvalidMatrix {
380 rows: 0,
381 cols: D + 1,
382 });
383 }
384
385 let mut m = Self::new(vertices.len(), D + 1, N::DEFAULT_NUMBER_TYPE)?;
386 for (i, v) in vertices.iter().enumerate() {
387 m.set_generator_type(i, true);
388 for (j, coord) in v.iter().enumerate() {
389 m.set(i, j + 1, coord);
390 }
391 }
392 Ok(m)
393 }
394
395 pub fn from_vertex_rows(vertices: &[Vec<N>]) -> CddResult<Self> {
400 let Some(first) = vertices.first() else {
401 return Err(CddWrapperError::InvalidMatrix { rows: 0, cols: 0 });
402 };
403
404 let dim = first.len();
405 if dim == 0 {
406 return Err(CddWrapperError::InvalidMatrix {
407 rows: vertices.len(),
408 cols: 0,
409 });
410 }
411
412 if vertices.iter().skip(1).any(|v| v.len() != dim) {
413 return Err(CddWrapperError::InvalidMatrix {
414 rows: vertices.len(),
415 cols: dim + 1,
416 });
417 }
418
419 let mut m = Self::new(vertices.len(), dim + 1, N::DEFAULT_NUMBER_TYPE)?;
420 for (row, coords) in vertices.iter().enumerate() {
421 m.set_generator_type(row, true);
422 for (col, coord) in coords.iter().enumerate() {
423 m.set(row, col + 1, coord);
424 }
425 }
426 Ok(m)
427 }
428
429 pub fn append_row(&self, coords: &[N], is_vertex: bool) -> CddResult<Self> {
430 if coords.len() + 1 != self.cols() {
431 return Err(CddWrapperError::InvalidMatrix {
432 rows: self.rows() + 1,
433 cols: coords.len() + 1,
434 });
435 }
436
437 let mut row = Self::new(1, self.cols(), self.number_type())?;
438 row.set_generator_type(0, is_vertex);
439 for (col, val) in coords.iter().enumerate() {
440 row.set(0, col + 1, val);
441 }
442
443 self.append_rows(&row)
444 }
445}
446
447#[derive(Debug)]
448pub struct SetFamily<N: CddNumber = DefaultNumber> {
449 raw: raw::SetFamilyData<N>,
450 _no_send_sync: PhantomData<Rc<()>>,
451}
452
453impl<N: CddNumber> SetFamily<N> {
454 pub unsafe fn from_raw(ptr: *mut c_void) -> Self {
460 SetFamily {
461 raw: raw::SetFamilyData::from_raw(ptr),
462 _no_send_sync: PhantomData,
463 }
464 }
465
466 pub fn as_raw(&self) -> *mut c_void {
467 self.raw.as_raw()
468 }
469
470 pub fn len(&self) -> usize {
471 self.raw.len()
472 }
473
474 pub fn is_empty(&self) -> bool {
475 self.len() == 0
476 }
477
478 pub fn universe_size(&self) -> usize {
479 self.raw.universe_size()
480 }
481
482 pub fn to_adjacency_lists(&self) -> Vec<Vec<usize>> {
483 self.raw.to_adjacency_lists()
484 }
485}
486
487impl<N: CddNumber> From<SetFamily<N>> for HbSetFamily {
488 fn from(value: SetFamily<N>) -> Self {
489 let universe = value.universe_size();
490 let n = value.len();
491
492 let mut sets: Vec<HbRowSet> = Vec::with_capacity(n);
493 for idx in 0..n {
494 let members = value.raw.members(idx);
495 sets.push(HbRowSet::from_indices(universe, &members));
496 }
497
498 HbSetFamily::from_sets(universe, sets)
499 }
500}
501
502#[derive(Debug)]
503pub struct Polyhedron<N: CddNumber = DefaultNumber> {
504 raw: raw::PolyhedronData<N>,
505 _no_send_sync: PhantomData<Rc<()>>,
506}
507
508impl<N: CddNumber> Polyhedron<N> {
509 pub fn from_matrix<R: RepresentationMarker>(m: &Matrix<N, R>) -> CddResult<Self> {
510 Ok(Self {
511 raw: raw::PolyhedronData::from_matrix(&m.raw)?,
512 _no_send_sync: PhantomData,
513 })
514 }
515
516 pub fn from_vertices<const D: usize>(vertices: &[[N; D]]) -> CddResult<Self> {
517 let m = Matrix::<N, Generator>::from_vertices(vertices)?;
518 Self::from_matrix(&m)
519 }
520
521 pub fn from_vertex_rows(vertices: &[Vec<N>]) -> CddResult<Self> {
522 let m = Matrix::<N, Generator>::from_vertex_rows(vertices)?;
523 Self::from_matrix(&m)
524 }
525
526 pub fn facets(&self) -> CddResult<Matrix<N, Inequality>> {
527 Ok(Matrix {
528 raw: self.raw.facets()?,
529 _no_send_sync: PhantomData,
530 _repr: PhantomData,
531 })
532 }
533
534 pub fn generators(&self) -> CddResult<Matrix<N, Generator>> {
535 Ok(Matrix {
536 raw: self.raw.generators()?,
537 _no_send_sync: PhantomData,
538 _repr: PhantomData,
539 })
540 }
541
542 #[deprecated(
543 note = "Uses cddlib's dd_CopyAdjacency, which has a serious performance-limiting bug that \
544causes an approximately superexponential slowdown for \"large\" numbers of generators. Prefer \
545hullabaloo's adjacency construction APIs instead."
546 )]
547 pub fn adjacency(&self) -> CddResult<SetFamily<N>> {
548 Ok(SetFamily {
549 raw: self.raw.adjacency()?,
550 _no_send_sync: PhantomData,
551 })
552 }
553
554 pub fn input_adjacency(&self) -> CddResult<SetFamily<N>> {
555 Ok(SetFamily {
556 raw: self.raw.input_adjacency()?,
557 _no_send_sync: PhantomData,
558 })
559 }
560
561 pub fn incidence(&self) -> CddResult<SetFamily<N>> {
562 Ok(SetFamily {
563 raw: self.raw.incidence()?,
564 _no_send_sync: PhantomData,
565 })
566 }
567
568 pub fn input_incidence(&self) -> CddResult<SetFamily<N>> {
569 Ok(SetFamily {
570 raw: self.raw.input_incidence()?,
571 _no_send_sync: PhantomData,
572 })
573 }
574
575 pub fn append_input_rows<R: RepresentationMarker>(
576 &mut self,
577 rows: &Matrix<N, R>,
578 ) -> CddResult<()> {
579 self.raw.append_input_rows(&rows.raw)
580 }
581
582 pub fn width_in_direction_real(&self, direction: &[f64]) -> CddResult<f64> {
583 let mut h_min = self.facets()?;
584 let mut h_max = h_min.clone_cdd()?;
585
586 if direction.len() + 1 != h_max.cols() {
587 return Err(CddWrapperError::InvalidMatrix {
588 rows: h_max.rows(),
589 cols: h_max.cols(),
590 });
591 }
592
593 let cols = h_max.cols();
594 let mut coeffs = vec![0.0f64; cols];
595
596 for (i, &u_i) in direction.iter().enumerate() {
597 coeffs[i + 1] = u_i;
598 }
599
600 h_max.set_objective_real(&coeffs);
601 let lp_max = Lp::<N>::from_matrix(&mut h_max, LpObjective::Maximize)?;
602 let sol_max = lp_max.solve()?;
603 let max_val = sol_max.opt_value_real();
604
605 h_min.set_objective_real(&coeffs);
606 let lp_min = Lp::<N>::from_matrix(&mut h_min, LpObjective::Minimize)?;
607 let sol_min = lp_min.solve()?;
608 let min_val = sol_min.opt_value_real();
609
610 Ok(max_val - min_val)
611 }
612}
613
614#[derive(Debug)]
615pub struct Lp<N: CddNumber = DefaultNumber> {
616 raw: raw::LpData<N>,
617 _no_send_sync: PhantomData<Rc<()>>,
618}
619
620#[derive(Debug)]
621pub struct LpSolution<N: CddNumber = DefaultNumber> {
622 raw: raw::LpSolutionData<N>,
623 _no_send_sync: PhantomData<Rc<()>>,
624}
625
626impl<N: CddNumber> Lp<N> {
627 pub fn from_matrix(matrix: &mut Matrix<N>, objective: LpObjective) -> CddResult<Self> {
628 Ok(Lp {
629 raw: raw::LpData::from_matrix(&mut matrix.raw, objective)?,
630 _no_send_sync: PhantomData,
631 })
632 }
633
634 pub fn solve(&self) -> CddResult<LpSolution<N>> {
635 Ok(LpSolution {
636 raw: self.raw.solve()?,
637 _no_send_sync: PhantomData,
638 })
639 }
640}
641
642impl<N: CddNumber> LpSolution<N> {
643 pub fn opt_value_real(&self) -> f64 {
644 self.raw.opt_value_real()
645 }
646}
647
648#[cfg(feature = "f64")]
649pub type MatrixF64 = Matrix<f64>;
650#[cfg(feature = "f64")]
651pub type PolyhedronF64 = Polyhedron<f64>;
652
653#[cfg(feature = "gmp")]
654pub type MatrixGmpFloat = Matrix<CddFloat>;
655#[cfg(feature = "gmp")]
656pub type PolyhedronGmpFloat = Polyhedron<CddFloat>;
657
658#[cfg(feature = "gmprational")]
659pub type MatrixGmpRational = Matrix<CddRational>;
660#[cfg(feature = "gmprational")]
661pub type PolyhedronGmpRational = Polyhedron<CddRational>;
662
663impl<N: CddNumber, R: RepresentationMarker> Matrix<N, R> {
664 pub fn convert<M: CddNumber>(&self) -> CddResult<Matrix<M, R>> {
665 convert_matrix_via_real::<N, M, R>(self)
666 }
667}
668
669#[cfg(all(feature = "f64", feature = "gmp"))]
670impl<R: RepresentationMarker> From<Matrix<f64, R>> for Matrix<CddFloat, R> {
671 fn from(value: Matrix<f64, R>) -> Self {
672 value
673 .convert()
674 .expect("Matrix<f64> -> Matrix<CddFloat> conversion failed")
675 }
676}
677
678#[cfg(all(feature = "f64", feature = "gmprational"))]
679impl<R: RepresentationMarker> From<Matrix<f64, R>> for Matrix<CddRational, R> {
680 fn from(value: Matrix<f64, R>) -> Self {
681 value
682 .convert()
683 .expect("Matrix<f64> -> Matrix<CddRational> conversion failed")
684 }
685}
686
687#[cfg(all(feature = "f64", feature = "gmp"))]
688impl<R: RepresentationMarker> From<Matrix<CddFloat, R>> for Matrix<f64, R> {
689 fn from(value: Matrix<CddFloat, R>) -> Self {
690 value
691 .convert()
692 .expect("Matrix<CddFloat> -> Matrix<f64> conversion failed")
693 }
694}
695
696#[cfg(all(feature = "f64", feature = "gmprational"))]
697impl<R: RepresentationMarker> From<Matrix<CddRational, R>> for Matrix<f64, R> {
698 fn from(value: Matrix<CddRational, R>) -> Self {
699 value
700 .convert()
701 .expect("Matrix<CddRational> -> Matrix<f64> conversion failed")
702 }
703}
704
705#[cfg(all(feature = "gmp", feature = "gmprational"))]
706impl<R: RepresentationMarker> From<Matrix<CddFloat, R>> for Matrix<CddRational, R> {
707 fn from(value: Matrix<CddFloat, R>) -> Self {
708 value
709 .convert()
710 .expect("Matrix<CddFloat> -> Matrix<CddRational> conversion failed")
711 }
712}
713
714#[cfg(all(feature = "gmp", feature = "gmprational"))]
715impl<R: RepresentationMarker> From<Matrix<CddRational, R>> for Matrix<CddFloat, R> {
716 fn from(value: Matrix<CddRational, R>) -> Self {
717 value
718 .convert()
719 .expect("Matrix<CddRational> -> Matrix<CddFloat> conversion failed")
720 }
721}
722
723fn convert_matrix_via_real<Src: CddNumber, Dst: CddNumber, R: RepresentationMarker>(
724 src: &Matrix<Src, R>,
725) -> CddResult<Matrix<Dst, R>> {
726 let rows = src.rows();
727 let cols = src.cols();
728 let mut out = Matrix::<Dst, R>::new(rows, cols, Dst::DEFAULT_NUMBER_TYPE)?;
729
730 for i in 0..rows {
731 for j in 0..cols {
732 out.set_real(i, j, src.get_real(i, j));
733 }
734 }
735
736 out.raw.copy_objective_from(&src.raw);
737
738 if let Some(coeffs) = src.raw.objective_row_coeffs_real() {
739 out.set_objective_real(&coeffs);
740 }
741
742 Ok(out)
743}