Skip to content

Commit 9284f42

Browse files
Support Complex literals, arithmetic operations (#2709)
Co-authored-by: Scott Carda <[email protected]>
1 parent f12b885 commit 9284f42

File tree

22 files changed

+952
-161
lines changed

22 files changed

+952
-161
lines changed

library/core/core.qs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,19 @@ namespace Std.Core {
5757
output
5858
}
5959

60-
export Length, Repeated;
60+
/// # Summary
61+
/// Represents a complex number by its real and imaginary components.
62+
/// The real component can be accessed via the `Real` field, and the imaginary
63+
/// component via the `Imag` field. Complex literals can be written using the
64+
/// form `a + bi`, where `a` is the Double literal for the real part and
65+
/// `b` is the Double literal for the imaginary part.
66+
///
67+
/// # Example
68+
/// The following snippet defines the imaginary unit 𝑖 = 0 + 1𝑖:
69+
/// ```qsharp
70+
/// let imagUnit = 1.0i;
71+
/// ```
72+
struct Complex { Real : Double, Imag : Double }
73+
74+
export Length, Repeated, Complex;
6175
}

library/std/src/Std/Math.qs

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,18 +1106,6 @@ function PNormalized(p : Double, array : Double[]) : Double[] {
11061106
// Complex numbers
11071107
//
11081108

1109-
/// # Summary
1110-
/// Represents a complex number by its real and imaginary components.
1111-
/// The first element of the tuple is the real component,
1112-
/// the second one - the imaginary component.
1113-
///
1114-
/// # Example
1115-
/// The following snippet defines the imaginary unit 𝑖 = 0 + 1𝑖:
1116-
/// ```qsharp
1117-
/// let imagUnit = Complex(0.0, 1.0);
1118-
/// ```
1119-
struct Complex { Real : Double, Imag : Double }
1120-
11211109
/// # Summary
11221110
/// Represents a complex number in polar form.
11231111
/// The polar representation of a complex number is c = r⋅𝑒^(t𝑖).
@@ -1218,7 +1206,7 @@ function ArgComplexPolar(input : ComplexPolar) : Double { input.Argument }
12181206
/// # Output
12191207
/// The unary negation of `input`.
12201208
function NegationC(input : Complex) : Complex {
1221-
Complex(-input.Real, -input.Imag)
1209+
-input
12221210
}
12231211

12241212
/// # Summary
@@ -1246,7 +1234,7 @@ function NegationCP(input : ComplexPolar) : ComplexPolar {
12461234
/// # Output
12471235
/// The sum a + b.
12481236
function PlusC(a : Complex, b : Complex) : Complex {
1249-
Complex(a.Real + b.Real, a.Imag + b.Imag)
1237+
a + b
12501238
}
12511239

12521240
/// # Summary
@@ -1261,12 +1249,7 @@ function PlusC(a : Complex, b : Complex) : Complex {
12611249
/// # Output
12621250
/// The sum a + b.
12631251
function PlusCP(a : ComplexPolar, b : ComplexPolar) : ComplexPolar {
1264-
ComplexAsComplexPolar(
1265-
PlusC(
1266-
ComplexPolarAsComplex(a),
1267-
ComplexPolarAsComplex(b)
1268-
)
1269-
)
1252+
ComplexAsComplexPolar(ComplexPolarAsComplex(a) + ComplexPolarAsComplex(b))
12701253
}
12711254

12721255
/// # Summary
@@ -1281,7 +1264,7 @@ function PlusCP(a : ComplexPolar, b : ComplexPolar) : ComplexPolar {
12811264
/// # Output
12821265
/// The difference a - b.
12831266
function MinusC(a : Complex, b : Complex) : Complex {
1284-
Complex(a.Real - b.Real, a.Imag - b.Imag)
1267+
a - b
12851268
}
12861269

12871270
/// # Summary
@@ -1311,10 +1294,7 @@ function MinusCP(a : ComplexPolar, b : ComplexPolar) : ComplexPolar {
13111294
/// # Output
13121295
/// The product a⋅b.
13131296
function TimesC(a : Complex, b : Complex) : Complex {
1314-
Complex(
1315-
a.Real * b.Real - a.Imag * b.Imag,
1316-
a.Real * b.Imag + a.Imag * b.Real
1317-
)
1297+
a * b
13181298
}
13191299

13201300
/// # Summary
@@ -1335,33 +1315,6 @@ function TimesCP(a : ComplexPolar, b : ComplexPolar) : ComplexPolar {
13351315
)
13361316
}
13371317

1338-
/// # Summary
1339-
/// Internal. Since it is easiest to define the power of two complex numbers
1340-
/// in Cartesian form as returning in polar form, we define that here, then
1341-
/// convert as needed.
1342-
/// Note that this is a multi-valued function, but only one value is returned.
1343-
internal function PowCAsCP(base : Complex, power : Complex) : ComplexPolar {
1344-
let (a, b) = (base.Real, base.Imag);
1345-
let (c, d) = (power.Real, power.Imag);
1346-
let baseSqNorm = a * a + b * b;
1347-
let baseNorm = Sqrt(baseSqNorm);
1348-
let baseArg = ArgComplex(base);
1349-
1350-
// We pick the principal value of the multi-valued complex function ㏑ as
1351-
// ㏑(a+b𝑖) = ln(|a+b𝑖|) + 𝑖⋅arg(a+b𝑖) = ln(baseNorm) + 𝑖⋅baseArg
1352-
// Therefore
1353-
// base^power = (a+b𝑖)^(c+d𝑖) = 𝑒^( (c+d𝑖)⋅㏑(a+b𝑖) ) =
1354-
// = 𝑒^( (c+d𝑖)⋅(ln(baseNorm)+𝑖⋅baseArg) ) =
1355-
// = 𝑒^( (c⋅ln(baseNorm) - d⋅baseArg) + 𝑖⋅(c⋅baseArg + d⋅ln(baseNorm)) )
1356-
// magnitude = 𝑒^((c⋅ln(baseNorm) - d⋅baseArg)) = baseNorm^c / 𝑒^(d⋅baseArg)
1357-
// angle = d⋅ln(baseNorm) + c⋅baseArg
1358-
1359-
let magnitude = baseNorm^c / E()^(d * baseArg);
1360-
let angle = d * Log(baseNorm) + c * baseArg;
1361-
1362-
ComplexPolar(magnitude, angle)
1363-
}
1364-
13651318
/// # Summary
13661319
/// Returns a number raised to a given power of type `Complex`.
13671320
/// Note that this is a multi-valued function, but only one value is returned.
@@ -1375,7 +1328,7 @@ internal function PowCAsCP(base : Complex, power : Complex) : ComplexPolar {
13751328
/// # Output
13761329
/// The power a^b
13771330
function PowC(a : Complex, power : Complex) : Complex {
1378-
ComplexPolarAsComplex(PowCAsCP(a, power))
1331+
a^power
13791332
}
13801333

13811334
/// # Summary
@@ -1391,7 +1344,7 @@ function PowC(a : Complex, power : Complex) : Complex {
13911344
/// # Output
13921345
/// The power a^b
13931346
function PowCP(a : ComplexPolar, power : ComplexPolar) : ComplexPolar {
1394-
PowCAsCP(ComplexPolarAsComplex(a), ComplexPolarAsComplex(power))
1347+
ComplexAsComplexPolar(ComplexPolarAsComplex(a)^ComplexPolarAsComplex(power))
13951348
}
13961349

13971350
/// # Summary
@@ -1406,11 +1359,7 @@ function PowCP(a : ComplexPolar, power : ComplexPolar) : ComplexPolar {
14061359
/// # Output
14071360
/// The quotient a / b.
14081361
function DividedByC(a : Complex, b : Complex) : Complex {
1409-
let sqNorm = b.Real * b.Real + b.Imag * b.Imag;
1410-
Complex(
1411-
(a.Real * b.Real + a.Imag * b.Imag) / sqNorm,
1412-
(a.Imag * b.Real - a.Real * b.Imag) / sqNorm
1413-
)
1362+
a / b
14141363
}
14151364

14161365
/// # Summary

source/compiler/qsc/src/interpret.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ impl Interpreter {
518518
if matches!(&namespace[..], &["Std", "OpenQASM", "Angle"]) && &*udt.name == "Angle" {
519519
*self.angle_ty_cache.borrow_mut() = Some(*item_id);
520520
UdtKind::Angle
521-
} else if matches!(&namespace[..], &["Std", "Math"]) && &*udt.name == "Complex" {
521+
} else if matches!(&namespace[..], &["Std", "Core"]) && &*udt.name == "Complex" {
522522
*self.complex_ty_cache.borrow_mut() = Some(*item_id);
523523
UdtKind::Complex
524524
} else {

source/compiler/qsc_ast/src/ast.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,8 @@ pub enum Lit {
16851685
Bool(bool),
16861686
/// A floating-point literal.
16871687
Double(f64),
1688+
/// A floating-point imaginary literal, e.g `1.0i`.
1689+
Imaginary(f64),
16881690
/// An integer literal.
16891691
Int(i64),
16901692
/// A Pauli operator literal.
@@ -1701,6 +1703,7 @@ impl Display for Lit {
17011703
Lit::BigInt(val) => write!(f, "BigInt({val})")?,
17021704
Lit::Bool(val) => write!(f, "Bool({val})")?,
17031705
Lit::Double(val) => write!(f, "Double({val})")?,
1706+
Lit::Imaginary(val) => write!(f, "Imaginary({val})")?,
17041707
Lit::Int(val) => write!(f, "Int({val})")?,
17051708
Lit::Pauli(val) => write!(f, "Pauli({val:?})")?,
17061709
Lit::Result(val) => write!(f, "Result({val:?})")?,

source/compiler/qsc_codegen/src/qsharp.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,14 @@ impl<W: Write> Visitor<'_> for QSharpGen<W> {
693693
};
694694
self.write(&num_str);
695695
}
696+
Lit::Imaginary(value) => {
697+
let num_str = if value.fract() == 0.0 {
698+
format!("{value}.i")
699+
} else {
700+
format!("{value}i")
701+
};
702+
self.write(&num_str);
703+
}
696704
Lit::Int(value) => self.write(&value.to_string()),
697705
Lit::Pauli(value) => match value {
698706
Pauli::I => self.write("PauliI"),

source/compiler/qsc_codegen/src/qsharp/tests.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,3 +1049,17 @@ fn bases_and_readable_values() {
10491049
}"#]],
10501050
);
10511051
}
1052+
1053+
#[test]
1054+
fn complex_literals() {
1055+
check(
1056+
"function Foo() : Complex { 3.0 + 4.0i }",
1057+
None,
1058+
&expect![[r#"
1059+
namespace test {
1060+
function Foo() : Complex {
1061+
3. + 4.i
1062+
}
1063+
}"#]],
1064+
);
1065+
}

source/compiler/qsc_doc_gen/src/generate_docs/tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ fn index_file_generation() {
181181
182182
| Name | Description |
183183
|------|-------------|
184+
| [Complex](xref:Qdk.Std.Core.Complex) | Represents a complex number by its real and imaginary components. The real component can be accessed via the `Real` field, and the imaginary component via the `Imag` field. Complex literals can be written using the form `a + bi`, where `a` is the Double literal for the real part and `b` is the Double literal for the imaginary part. |
184185
| [Length](xref:Qdk.Std.Core.Length) | Returns the number of elements in the input array `a`. |
185186
| [Repeated](xref:Qdk.Std.Core.Repeated) | Creates an array of given `length` with all elements equal to given `value`. `length` must be a non-negative integer. |
186187
"#]]
@@ -267,6 +268,16 @@ fn generates_std_core_summary() {
267268
let combined_summary = core_summaries.join("\n\n");
268269

269270
expect![[r#"
271+
## Complex
272+
273+
```qsharp
274+
struct Complex { Real : Double, Imag : Double }
275+
```
276+
277+
Represents a complex number by its real and imaginary components. The real component can be accessed via the `Real` field, and the imaginary component via the `Imag` field. Complex literals can be written using the form `a + bi`, where `a` is the Double literal for the real part and `b` is the Double literal for the imaginary part.
278+
279+
280+
270281
## Length
271282
272283
```qsharp

0 commit comments

Comments
 (0)