Complex Numbers – Inner Product

The complex inner product is the dot product of two complex numbers. This value is calculated by multiplying the first complex number with the conjugate of the second complex number.

For example, the complex conjugate of 7 + 3i would be 7 - 3i. The complex inner product of these two numbers would be:

(7 * 7) + (3i * -3i) = 49 + (-9 * -1) = 58.

1. Complex Inner Product (2 vectors)

This program takes two text files, each containing an equal number of newline-separated complex numbers, and outputs a text file containing the sum of the complex inner products between each of the two files.

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <complex>

#define PI 3.14159265359

int main()
{
  // Introduction
  std::cout << "This program calculates the complex inner product of two vectors given by text files." << std::endl;
  std::cout << "======================================================" << std::endl;

  // Prompt user for file 1
  std::string filename1;
  std::cout << "Please enter the name of the text file (with extension .txt) containing the first input: " << std::flush;
  std::ifstream in1;
  while (true)
  {
    in1.close();
    in1.clear();
    std::getline(std::cin, filename1);
    in1.open(filename1.c_str());
    if (in1)
    {
      break;
    }
    std::cout << "Invalid file. Please enter a valid input file name: " << std::flush; 
  }
  std::cout << std::endl;

  // Count the number of complex values (lines) in file 1
  // Assign to N1
  int N1 = 0;
  std::string line1;
  std::vector<std::complex<double>> vec1;

  while (std::getline(in1, line1))
  {
    ++N1;
    // Parse the real and imaginary parts of each complex value
    std::string re_s, im_s;
    double re, im;
    std::string::size_type pos = line1.find('+');
    std::string::size_type neg = line1.find('-');
    pos = std::min(pos, neg);

    char sign = line1[pos];  // '+' or '-'
    re_s = line1.substr(0, pos);
    re = stod(re_s);

    std::string::size_type i_pos = line1.find('i');

    im_s = line1.substr(pos + 1, i_pos - pos - 1);
    im = stod(im_s);
    if (sign == '-')
    {
      im *= -1;
    }
    std::complex<double> mycomplex(re, im);
    // Store them in a vector
    vec1.push_back(mycomplex);
  }

  // Close file 1 stream
  in1.close();
  in1.clear();

  // Prompt user for file 2
  std::string filename2;
  std::cout << "Please enter the name of the text file (with extension .txt) containing the first input: " << std::flush;
  std::ifstream in2;
  while (true)
  {
    in2.close();
    in2.clear();
    std::getline(std::cin, filename2);
    in2.open(filename2.c_str());
    if (in2)
    {
      break;
    }
    std::cout << "Invalid file. Please enter a valid input file name: " << std::flush; 
  }
  std::cout << std::endl;

  // Count the number of complex values (lines) in file 2
  // Assign to N2
  int N2 = 0;
  std::string line2;
  std::vector<std::complex<double>> vec2;

  while (std::getline(in2, line2))
  {
    ++N2;
    // Parse the real and imaginary parts of each complex value
    std::string re_s, im_s;
    double re, im;
    std::string::size_type pos = line2.find('+');
    std::string::size_type neg = line2.find('-');
    pos = std::min(pos, neg);

    char sign = line2[pos];  // '+' or '-'
    re_s = line2.substr(0, pos);
    re = stod(re_s);

    std::string::size_type i_pos = line2.find('i');

    im_s = line2.substr(pos + 1, i_pos - pos - 1);
    im = stod(im_s);
    if (sign == '-')
    {
      im *= -1;
    }
    std::complex<double> mycomplex(re, im);
    // Store them in a vector
    vec2.push_back(mycomplex);
  }

  // Close file 2 stream
  in2.close();
  in2.clear();

  // Make sure N1 and N2 are equal
  if (N1 != N2)
  {
    std::cout << "The files have different amounts of complex numbers! Try again." << std::endl;
    return 0;
  }

  // Do the math
  std::complex<double> sum;
  for (int i = 0; i < N1; ++i)
  {
    // Convert vec2's complex numbers into conjugates
    // Multiply it with the number from vec1
    sum += vec1[i] * std::conj(vec2[i]);
  }

  // File output
  std::string::size_type dot_pos1 = filename1.find('.');
  std::string input1 = filename1.substr(0, dot_pos1);
  std::string::size_type dot_pos2 = filename2.find('.');
  std::string input2 = filename2.substr(0, dot_pos2);

  std::ofstream output_file("out_dot_" + std::to_string(N1) + "_" + input1 + "_" + input2 + ".txt");
  output_file << sum.real() << "+" << sum.imag() << "i" << "\n";
  std::cout << "==================================================" << std::endl;
  std::cout << "Calculation complete. Please check your directory for the output file." << std::endl;
  output_file.close();
  return 0;
}

Given the following two input files:

1+1i
2-1i
3+1i
1-3i

The following output is generated:

9+7i

2. Complex Inner Product (vector with its complex roots of unity)

This program is similar to the previous one, but instead takes a single input file, generates its complex roots of unity, and outputs the sum of the complex inner products between those two vectors.

Each entry in the vector for complex roots of unity is calculated as the polar coordinate e2*pi*i/N, where N is the total number of complex numbers and i is an index from 1 to N.

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <complex>

#define PI 3.14159265359

int main()
{
  // Introduction
  std::cout << "This program calculates the complex inner product of two vectors." << std::endl;
  std::cout << "======================================================" << std::endl;

  // Prompt user for input file
  std::string filename;
  std::cout << "Please enter the name of the text file (with extension .txt) containing the input: " << std::flush;
  std::ifstream in;
  while (true)
  {
    in.close();
    in.clear();
    std::getline(std::cin, filename);
    in.open(filename.c_str());
    if (in)
    {
      break;
    }
    std::cout << "Invalid file. Please enter a valid input file name: " << std::flush; 
  }
  std::cout << std::endl;

  // Count the number of complex values (lines) in the file
  // Assign to N
  int N = 0;
  std::string line;
  std::vector<std::complex<double>> vec1;

  while (std::getline(in, line))
  {
    ++N;
    // Parse the real and imaginary parts of each complex value
    std::string re_s, im_s;
    double re, im;
    std::string::size_type pos = line.find('+');
    std::string::size_type neg = line.find('-');
    pos = std::min(pos, neg);

    char sign = line[pos];  // '+' or '-'
    re_s = line.substr(0, pos);
    re = stod(re_s);

    std::string::size_type i_pos = line.find('i');

    im_s = line.substr(pos + 1, i_pos - pos - 1);
    im = stod(im_s);
    if (sign == '-')
    {
      im *= -1;
    }
    std::complex<double> mycomplex(re, im);
    // Store them in a vector
    vec1.push_back(mycomplex);
  }
  std::cout << "There are " << N << " complex numbers in the file." << std::endl;

  // Close file  stream
  in.close();
  in.clear();

  // Construct the complex vector of roots of unity
  std::vector<std::complex<double>> vec2;
  std::complex<double> first_val(1,0);
  vec2.push_back(first_val);
  for (int i = 1; i < N; ++i)
  {
    vec2.push_back(std::polar(1.0, 2 * PI * i / N));
  }

  // Then calculate the inner product
  std::complex<double> sum;
  for (int i = 0; i < N; ++i)
  {
    // Convert vec2's complex numbers into conjugates
    // Multiply it with the number from vec1
    sum += vec1[i] * std::conj(vec2[i]);
  }

  // Output file
  std::string::size_type dot_pos = filename.find('.');
  std::string input = filename.substr(0, dot_pos);

  std::ofstream output_file("out_prod_" + std::to_string(N) + "_" + input + ".txt");
  output_file << sum.real() << "+" << sum.imag() << "i" << "\n";
  std::cout << "==================================================" << std::endl;
  std::cout << "Calculation complete. Please check your directory for the output file." << std::endl;
  output_file.close();

  return 0;
}

Given the following input:

1+1i
2-1i

The following input is generated:

-1+2i