@@ -19,6 +19,8 @@ use approx;
1919use crossterm:: terminal;
2020use hashbrown:: HashSet ;
2121use itertools:: { Itertools , MinMaxResult } ;
22+ use lexical_core:: ToLexicalWithOptions ;
23+ use lexical_write_float:: { self , format:: STANDARD } ;
2224use pyo3:: prelude:: * ;
2325use std:: fmt:: Debug ;
2426use std:: ops:: Index ;
@@ -60,7 +62,7 @@ pub fn draw_circuit(
6062 if approx:: abs_diff_eq!( * f, 0. ) {
6163 String :: new ( )
6264 } else {
63- format ! ( "global phase: {}\n " , f )
65+ format ! ( "global phase: {}\n " , F64UiFormatter :: new ( 5 ) . format ( * f ) )
6466 }
6567 }
6668 Param :: ParameterExpression ( expr) => {
@@ -669,6 +671,43 @@ impl TextWireElement {
669671 }
670672}
671673
674+ /// A simple formatter for rendering nicely-truncated floating-point numbers,
675+ /// in a way similar to Python's g or printf's %g format specifiers.
676+ /// Example outputs:
677+ /// F64UiFormatter::new(4).format(1.23456) --> 1.235
678+ /// F64UiFormatter::new(4).format(123.456) --> 123.5
679+ /// F64UiFormatter::new(5).format(12345678f64) --> 1.2346e7
680+ /// F64UiFormatter::new(5).format(-0.00001234) --> -1.234e-5
681+ struct F64UiFormatter {
682+ buffer : Vec < u8 > ,
683+ options : lexical_write_float:: Options ,
684+ }
685+
686+ impl F64UiFormatter {
687+ fn new ( num_significant_digits : i32 ) -> Self {
688+ let options = lexical_write_float:: Options :: builder ( )
689+ . max_significant_digits ( core:: num:: NonZeroUsize :: new (
690+ num_significant_digits as usize ,
691+ ) )
692+ . positive_exponent_break ( core:: num:: NonZeroI32 :: new ( num_significant_digits) )
693+ . negative_exponent_break ( core:: num:: NonZeroI32 :: new ( -num_significant_digits + 1 ) )
694+ . trim_floats ( true )
695+ . build_strict ( ) ;
696+
697+ F64UiFormatter {
698+ buffer : vec ! [ 0u8 ; options. buffer_size_const:: <f64 , STANDARD >( ) ] ,
699+ options,
700+ }
701+ }
702+
703+ /// Formats the input number based on the formatting options.
704+ /// This Can be called multiple times, but the internal buffer is overwritten on each call.
705+ fn format ( & mut self , num : f64 ) -> & str {
706+ let buf = num. to_lexical_with_options :: < STANDARD > ( & mut self . buffer , & self . options ) ;
707+ std:: str:: from_utf8_mut ( buf) . expect ( "Byte representation should be valid" )
708+ }
709+ }
710+
672711pub const Q_WIRE : char = '─' ;
673712pub const C_WIRE : char = '═' ;
674713pub const TOP_CON : char = '┴' ;
@@ -731,7 +770,11 @@ impl TextDrawer {
731770 StandardInstruction :: Delay ( delay_unit) => {
732771 match instruction. params_view ( ) . first ( ) . unwrap ( ) {
733772 Param :: Float ( duration) => {
734- format ! ( "Delay({}[{}])" , duration, delay_unit)
773+ format ! (
774+ "Delay({}[{}])" ,
775+ F64UiFormatter :: new( 5 ) . format( * duration) ,
776+ delay_unit
777+ )
735778 }
736779 Param :: ParameterExpression ( expr) => {
737780 format ! ( "Delay({}[{}])" , expr, delay_unit)
@@ -763,7 +806,7 @@ impl TextDrawer {
763806 . params_view ( )
764807 . iter ( )
765808 . map ( |param| match param {
766- Param :: Float ( f) => f . to_string ( ) ,
809+ Param :: Float ( f) => F64UiFormatter :: new ( 5 ) . format ( * f ) . to_string ( ) ,
767810 Param :: ParameterExpression ( expr) => expr. to_string ( ) ,
768811 _ => format ! ( "{:?}" , param) ,
769812 } )
@@ -1902,4 +1945,51 @@ q_1: ┤ Ry(🎩) ├┤1 ├┤ 💶🔉(🎩) ├┤1 ├
19021945" ;
19031946 assert_eq ! ( result, expected. trim_start_matches( "\n " ) ) ;
19041947 }
1948+
1949+ #[ cfg( not( miri) ) ]
1950+ #[ test]
1951+ fn test_f64_formatting ( ) {
1952+ let qubits = vec ! [
1953+ ShareableQubit :: new_anonymous( ) ,
1954+ ShareableQubit :: new_anonymous( ) ,
1955+ ] ;
1956+ let mut circuit = CircuitData :: new ( Some ( qubits) , None , Param :: Float ( 12.3 ) ) . unwrap ( ) ;
1957+
1958+ circuit
1959+ . push_standard_gate ( StandardGate :: RX , & [ Param :: Float ( 1.234567 ) ] , & [ Qubit ( 0 ) ] )
1960+ . unwrap ( ) ;
1961+ circuit
1962+ . push_standard_gate ( StandardGate :: RX , & [ Param :: Float ( 123.4567 ) ] , & [ Qubit ( 0 ) ] )
1963+ . unwrap ( ) ;
1964+
1965+ let expr = ParameterExpression :: from_symbol ( Symbol :: new ( "ϕ" , None , None ) )
1966+ . mul ( & ParameterExpression :: from_f64 ( 1.23456 ) )
1967+ . unwrap ( ) ;
1968+ let param = Param :: ParameterExpression ( Arc :: new ( expr) ) ;
1969+ circuit
1970+ . push_standard_gate ( StandardGate :: RY , & [ param] , & [ Qubit ( 0 ) ] )
1971+ . unwrap ( ) ;
1972+ circuit
1973+ . push_standard_gate ( StandardGate :: RZ , & [ Param :: Float ( 123456789f64 ) ] , & [ Qubit ( 1 ) ] )
1974+ . unwrap ( ) ;
1975+
1976+ circuit
1977+ . push_standard_gate ( StandardGate :: RX , & [ Param :: Float ( 0.1234567 ) ] , & [ Qubit ( 1 ) ] )
1978+ . unwrap ( ) ;
1979+ circuit
1980+ . push_standard_gate ( StandardGate :: RX , & [ Param :: Float ( 0.0000123456 ) ] , & [ Qubit ( 1 ) ] )
1981+ . unwrap ( ) ;
1982+
1983+ let result = draw_circuit ( & circuit, true , true , None ) . unwrap ( ) ;
1984+ let expected = "
1985+ global phase: 6.0168
1986+ ┌────────────┐ ┌────────────┐ ┌───────────────┐
1987+ q_0: ─┤ Rx(1.2346) ├─┤ Rx(123.46) ├─┤ Ry(1.23456*ϕ) ├
1988+ ┌┴────────────┴┐├────────────┴┐├───────────────┤
1989+ q_1: ┤ Rz(1.2346e8) ├┤ Rx(0.12346) ├┤ Rx(1.2346e-5) ├
1990+ └──────────────┘└─────────────┘└───────────────┘
1991+ " ;
1992+
1993+ assert_eq ! ( result, expected. trim_start_matches( "\n " ) ) ;
1994+ }
19051995}
0 commit comments