Tamil
மொழிகள்
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

Note

இந்த பக்கம் tutorials/circuits_advanced/06_building_pulse_schedules.ipynb இலிருந்து உருவாக்கப்பட்டது.

பல்ஸ் அட்டவணை உருவாக்குதல்

பல்ஸ் கேட்கள் ஒரு சுற்று கேட்டுக்கு குறைந்த அளவிலான, சரியான பிரதிநிதித்துவத்தை வரையறுக்கின்றன. பல்ஸ் ப்ரோக்ராமுடன் ஒரு ஒற்றை செயல்பாட்டை செயல்படுத்த முடியும், இது பல குறைந்த-நிலை வழிமுறைகளைக் கொண்டுள்ளது. பல்ஸ் கேட்ளைப் பற்றி மேலும் அறிய, இந்த ஆவணத்தைப் பார்க்கவும். பல்ஸ் ப்ரோக்ராம்களை எவ்வாறு உருவாக்குவது என்பதை இந்த பக்கம் விவரிக்கிறது.

குறிப்பு: IBM சாதனங்களுக்கு, கேட்களை விவரிக்க பல்ஸ் ப்ரோக்ராம்கள் சப்ரூட்டின்களாகப் பயன்படுத்தப்படுகின்றன. முன்னதாக, சில சாதனங்கள் இந்த வடிவமைப்பில் முழு ப்ரோக்ராம்களையும் ஏற்றுக்கொண்டன, ஆனால் இது டிசம்பர் 2021 இல் நிறுத்தப்பட்டது. பிற வழங்குநர்கள் இந்த வடிவமைப்பில் முழு ப்ரோக்ராம்களையும் ஏற்கலாம். ப்ரோக்ராம் எவ்வாறு பயன்படுத்தப்படுகிறது என்பதைப் பொருட்படுத்தாமல், ப்ரோக்ராம்மை உருவாக்குவதற்கான தொடரியல் ஒன்றுதான். எப்படி என்பதை அறிய படிக்கவும்!

Pulse programs, which are called Schedules, describe instruction sequences for the control electronics. We build Schedules using the Pulse Builder. It’s easy to initialize a schedule:

[20]:
from qiskit import pulse

with pulse.build(name='my_example') as my_program:
    # Add instructions here
    pass

my_program
[20]:
ScheduleBlock(, name="my_example", transform=AlignLeft())

இதுவரை எந்த வழிமுறைகளும் இல்லை என்பதை நீங்கள் காணலாம். இந்த பக்கத்தின் அடுத்த பகுதி நீங்கள் ஒரு அட்டவணையில் சேர்க்கக்கூடிய ஒவ்வொரு வழிமுறைகளையும் விளக்கும், மேலும் கடைசி பகுதி பல்வேறு alignment contexts விவரிக்கும், இது ஒருவருக்கொருவர் தொடர்புடைய நேரத்தில் அறிவுறுத்தல்கள் எவ்வாறு வைக்கப்படுகின்றன என்பதை தீர்மானிக்கிறது.

Schedule வழிமுறைகள்

ஒவ்வொரு வழிமுறை வகை அதன் தனிப்பட்ட அமைப்புகளைக் கொண்டது. நீங்கள் மேலே பார்க்க முடியும், அவர்கள் ஒவ்வொன்றும் குறைந்தபட்சம் ஒரு Channel குறிப்பிடுவது எங்கே பயன்பாடு செய்யப்படும்.

Channels என்பது கட்டுப்பாட்டு வன்பொருளிலிருந்து குவாண்டம் சில்லுக்கான சமிக்ஞை வரிகளுக்கான லேபிள்கள்.

  • DriveChannelகள் பொதுவாக driving ஒற்றை க்யூபிட் சுழற்சிகளுக்குப் பயன்படுத்தப்படுகின்றன,

  • ControlChannelகள் பொதுவாக மல்டி-க்யூபிட் கேட்கள் அல்லது ட்யூனபிள் க்யூபிட்களுக்கான கூடுதல் டிரைவ் கோடுகள்,

  • MeasureChannelகள் வாசிப்பைத் தூண்டும் பல்ஸ்களைக் கடத்துவதற்கு குறிப்பிட்டவை, மற்றும்

  • AcquireChannelகள் ரீட்அவுட் சிக்னல்களை சேகரிக்கும் டிஜிட்டலைசர்களைத் தூண்டுவதற்குப் பயன்படுத்தப்படுகின்றன.

DriveChannelகள், ControlChannelகள், மற்றும் MeasureChannelகள், இவை எல்லாம் PulseChannelகள்; இதன் பொருள் அவை transmitting பல்ஸ்களை ஆதரிக்கின்றன, அதேசமயம் AcquireChannel என்பது பெறும் சேனல் மட்டுமே, மேலும் அலைவடிவங்களை இயக்க முடியாது.

பின்வரும் எடுத்துக்காட்டுகளுக்கு, PulseChannel ஏற்றுக்கொள்ளும் ஒவ்வொரு Instruction-க்கும் ஒரு DriveChannel உதாரணத்தை உருவாக்குவோம். சேனல்கள் ஒரு முழு எண் index ஆர்கியுமென்ட்டை எடுத்துக்கொள்கின்றன. ControlChannelகள் தவிர, குறியீட்டு எண்கள் க்யூபிட் அடையாளக் குறியீட்டையே அற்பமாக வரையருக்கிறது.

[21]:
from qiskit.pulse import DriveChannel

channel = DriveChannel(0)

துடிப்பு Schedule அது இயங்கும் பின்தளத்தில் இருந்து சுயாதீனமாக உள்ளது. எவ்வாறாயினும், எங்கள் திட்டத்தை pulse.build க்கு வழங்குவதன் மூலம் இலக்கு பின்தளத்தில் தெரிந்த சூழலில் உருவாக்க முடியும். முடிந்தால் நீங்கள் ஒரு பின்தளத்தில் வழங்க வேண்டும். சேனல் அணுகல்களைப் பயன்படுத்துவதன் மூலம் pulse.<type>_channel(<idx>) நாங்கள் கிடைக்கக்கூடிய சாதன வளங்களை மட்டுமே பயன்படுத்துகிறோம் என்பதை உறுதிப்படுத்த முடியும்.

[22]:
from qiskit.providers.fake_provider import FakeValencia

backend = FakeValencia()

with pulse.build(backend=backend, name='backend_aware') as backend_aware_program:
    channel = pulse.drive_channel(0)
    print(pulse.num_qubits())
    # Raises an error as backend only has 5 qubits
    #pulse.drive_channel(100)
5

delay

நாம் உருவாக்கக்கூடிய எளிய வழிமுறைகளில் ஒன்று delay. இது ஒரு தடுக்கும் அறிவுறுத்தலாகும், இது குறிப்பிட்ட காலத்திற்கு குறிப்பிட்ட சேனலில் எந்த சிக்னலையும் வெளியிட கட்டுப்பாட்டு மின்னணுவியலைக் கூறுகிறது. பிற அறிவுறுத்தல்களின் நேரத்தைக் கட்டுப்படுத்த இது பயனுள்ளதாக இருக்கும்.

The duration here and elsewhere is in terms of the backend’s cycle time (1 / sample rate), dt. It must take an integer value.

delay அறிவுறுத்தலைச் சேர்க்க, நாங்கள் ஒரு கால அளவையும் ஒரு சேனலையும் கடந்து செல்கிறோம், அங்கு channel என்பது AcquireChannel உட்பட எந்தவொரு சேனலாகவும் இருக்கலாம். பல்ஸ் பில்டர் சூழலைத் தொடங்க pulse.build ஐப் பயன்படுத்துகிறோம். இது delay_5dt அட்டவணையில் தானாகவே நம் தாமதத்தைத் திட்டமிடுகிறது.

[23]:
with pulse.build(backend) as delay_5dt:
    pulse.delay(5, channel)

That’s all there is to it. Any instruction added after this delay on the same channel will execute five timesteps later than it would have without this delay.

play

The play instruction is responsible for executing pulses. It’s straightforward to add a play instruction:

with pulse.build() as sched:
    pulse.play(pulse, channel)

Let’s clarify what the pulse argument is and explore a few different ways to build one.

பல்ஸ்கள்

ஒரு Pulse ஒரு தன்னிச்சையான பல்ஸ் envelope ஐக் குறிக்கிறது. வெளியீட்டு அலைவடிவத்தின் பண்பேற்றம் அதிர்வெண் மற்றும் கட்டம் set_frequency மற்றும் shift_phase அறிவுறுத்தல்களால் கட்டுப்படுத்தப்படுகின்றன, அவை அடுத்ததாக நாம் காண்போம்.

The image below may provide some intuition for why they are specified separately. Think of the pulses which describe their envelopes as input to an arbitrary waveform generator (AWG), a common lab instrument – this is depicted in the left image. Notice the limited sample rate discritizes the signal. The signal produced by the AWG may be mixed with a continuous sine wave generator. The frequency of its output is controlled by instructions to the sine wave generator; see the middle image. Finally, the signal sent to the qubit is demonstrated by the right side of the image below.

குறிப்பு: வன்பொருள் பிற வழிகளில் செயல்படுத்தப்படலாம், ஆனால் நாம் வழிமுறைகளை தனித்தனியாக வைத்திருந்தால், பண்பேற்றம் அதிர்வெண்ணின் மதிப்பு போன்ற வெளிப்படையான தகவல்களை இழப்பதைத் தவிர்க்கிறோம்.

alt text

There are many methods available to us for building up pulses. Our library within Qiskit Pulse contains helpful methods for building Pulses. Let’s take for example a simple Gaussian pulse – a pulse with its envelope described by a sampled Gaussian function. We arbitrarily choose an amplitude of 1, standard deviation \(\sigma\) of 10, and 128 sample points.

Note: The amplitude norm is arbitrarily limited to 1.0. Each backend system may also impose further constraints – for instance, a minimum pulse size of 64. These additional constraints, if available, would be provided through the BackendConfiguration which is described here.

[24]:
from qiskit.pulse import library

amp = 1
sigma = 10
num_samples = 128

அளவுரு பல்ஸ்கள்

Let’s build our Gaussian pulse using the Gaussian parametric pulse. A parametric pulse sends the name of the function and its parameters to the backend, rather than every individual sample. Using parametric pulses makes the jobs you send to the backend much smaller. IBM Quantum backends limit the maximum job size that they accept, so parametric pulses may allow you to run larger programs.

library -யில் மற்ற அளவுரு பல்ஸ்களில் GaussianSquare, Drag, மற்றும் Constant ஆகியவை அடங்கும்.

குறிப்பு: அளவுரு பல்ஸ்களை எவ்வாறு மாதிரியாக்குவது என்பதைத் தீர்மானிப்பது பின்தளத்தின் பொறுப்பாகும். அளவுரு பல்ஸ்களை வரைய முடியும், ஆனால் காட்டப்படும் மாதிரிகள் பின்தளத்தில் செயல்படுத்தப்பட்டதைப் போலவே இருக்கும் என்று உத்தரவாதம் அளிக்கப்படவில்லை.

[25]:
gaus = pulse.library.Gaussian(num_samples, amp, sigma,
                              name="Parametric Gaus")
gaus.draw()
[25]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_12_0.png

மாதிரிகளால் விவரித்த பல்ஸ் அலைவடிவங்கள்

ஒரு Waveform என்பது நேர-வரிசைப்படுத்தப்பட்ட சிக்கலான பெருக்கங்கள் அல்லது மாதிரிகள் என குறிப்பிடப்படும் பல்ஸ் சிக்னலாகும். ஒவ்வொரு மாதிரியும் ஒரு சுழற்சிக்காக இயக்கப்படுகிறது, இது ஒரு பின்தளத்தில் தீர்மானிக்கப்படும் dt. எங்கள் திட்டத்தின் நிகழ்நேர இயக்கவியல் பற்றி அறிய விரும்பினால், dt இன் மதிப்பை நாம் அறிந்து கொள்ள வேண்டும். ஒரு (பூஜ்ஜிய-குறியீட்டு) \(i^{th}\) மாதிரியானது, நேரம் i*dt முதல் (i + 1)*dt வரை, க்யூபிட் அதிர்வெண்ணால் மாற்றியமைக்கப்படும்.

[26]:
import numpy as np

times = np.arange(num_samples)
gaussian_samples = np.exp(-1/2 *((times - num_samples / 2) ** 2 / sigma**2))

gaus = library.Waveform(gaussian_samples, name="WF Gaus")
gaus.draw()
[26]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_14_0.png

பல்ஸ் நூலக செயல்பாடுகள்

எங்கள் சொந்த துடிப்பு நூலகத்தில் பொதுவான செயல்பாடுகளிலிருந்து Waveform உருவாக்க மாதிரி முறைகள் உள்ளன.

[27]:
gaus = library.gaussian(duration=num_samples, amp=amp, sigma=sigma, name="Lib Gaus")
gaus.draw()
[27]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_16_0.png

உங்கள் pulse ஐக் குறிப்பிட நீங்கள் எந்த முறையைப் பயன்படுத்தினாலும், play உங்கள் அட்டவணையில் அதே வழியில் சேர்க்கப்படுகிறது:

[28]:
with pulse.build() as schedule:
    pulse.play(gaus, channel)
schedule.draw()
[28]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_18_0.png

நீங்கள் ஒரு சிக்கலான பட்டியல் அல்லது வரிசையை நேரடியாக play க்கு வழங்கலாம்

[29]:
with pulse.build() as schedule:
    pulse.play([0.001*i for i in range(160)], channel)
schedule.draw()
[29]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_20_0.png

play அறிவுறுத்தல் அதன் கால அளவை அதன் Pulse இலிருந்து பெறுகிறது: அளவுருவாக்கப்பட்ட பல்ஸின் காலம் வெளிப்படையான ஆர்க்யுமென்ட் ஆகும், மேலும் Waveform காலம் உள்ளீட்டு மாதிரிகளின் எண்ணிக்கை.

set_frequency

முன்பு விளக்கியது போல, வெளியீட்டு துடிப்பு அலைவடிவ உறை ஒரு அதிர்வெண் மற்றும் கட்டத்தால் மாற்றியமைக்கப்படுகிறது. ஒவ்வொரு சேனலுக்கும் default frequency listed in the backend.defaults() இல் பட்டியலிடப்பட்டுள்ளது.

சேனலின் அதிர்வெண் எந்த நேரத்திலும் Schedule க்குள் set_frequency அறிவுறுத்தலால் புதுப்பிக்கப்படலாம். இது ஒரு float frequency மற்றும் PulseChannel channel ஆகியவற்றை உள்ளீடாக எடுக்கும். set_frequency அறிவுறுத்தலைத் தொடர்ந்து ஒரு சேனலில் உள்ள அனைத்து பல்ஸ்களும் மற்றொரு set_frequency அறிவுறுத்தல் வரும் வரை அல்லது நிரல் முடியும் வரை கொடுக்கப்பட்ட அதிர்வெண் மூலம் மாற்றியமைக்கப்படும்.

அறிவுறுத்தலுக்கு 0 என்ற மறைமுக காலம் உள்ளது.

குறிப்பு: கோரக்கூடிய அதிர்வெண்கள் மொத்த அலைவரிசை மற்றும் ஒவ்வொரு வன்பொருள் சேனலின் உடனடி அலைவரிசை ஆகியவற்றால் வரையறுக்கப்படுகின்றன. எதிர்காலத்தில், இவை backend -ல் புகாரளிக்கப்படும்.

[30]:
with pulse.build(backend) as schedule:
    pulse.set_frequency(4.5e9, channel)

shift_phase

shift_phase அறிவுறுத்தல் அதிர்வெண் பண்பேற்றத்தின் கட்டத்தை phase அதிகரிக்கும். set_frequency ஐப் போலவே, இந்த கட்ட மாற்றமும் நிரல் முடியும் வரை ஒரே சேனலில் பின்வரும் அனைத்து வழிமுறைகளையும் பாதிக்கும். shift_phase இன் பாதிப்பைச் செயல்தவிர்க்க, எதிர்மறையான phase ஒரு புதிய அறிவுறுத்தலுக்கு அனுப்பப்படலாம்.

set_frequency போலவே, அறிவுறுத்தலும் 0 இன் மறைமுக காலத்தைக் கொண்டுள்ளது.

[31]:
with pulse.build(backend) as schedule:
    pulse.shift_phase(np.pi, channel)

acquire

acquire அறிவுறுத்தல் வாசிப்புக்கான தரவு கையகப்படுத்துதலைத் தூண்டுகிறது. சில கால அவகாசம் எடுத்துக்கொண்டு AcquireChannel அளவிடப்படும் க்யூபிட்டை வரையறுக்கப்படுகிறது, மற்றும் ஒரு MemorySlot அல்லது RegisterSlot. MemorySlot என்பது கிளாசிக்கல் நினைவகம், அங்கு ரீட்அவுட் முடிவு சேமிக்கப்படும். கட்டுப்பாட்டு மின்னணுவியலில் ஒரு பதிவேட்டில் RegisterSlot வரையறுக்கப்படும், இது விரைவான பின்னூட்டத்திற்கான ரீட்அவுட் முடிவை சேமிக்கிறது.

acquire வழிமுறைகள் தனிப்பயன் Discriminator`கள் மற்றும் Kernelகள் முக்கிய ஆர்க்யுமென்ட்களாக எடுத்துக் கொள்ளலாம்.

[32]:
from qiskit.pulse import Acquire, AcquireChannel, MemorySlot

with pulse.build(backend) as schedule:
    pulse.acquire(1200, pulse.acquire_channel(0), MemorySlot(0))

Now that we know how to add Schedule instructions, let’s learn how to control exactly when they’re played.

பல்ஸ் பில்டர்

இங்கே, அட்டவணைகளை எவ்வாறு உருவாக்குவது என்பதைக் கற்றுக்கொள்வதற்கான மிக முக்கியமான பல்ஸ் பில்டர் அம்சங்களைப் பார்ப்போம். இது முழுமையானது அல்ல; பல்ஸ் பில்டரைப் பயன்படுத்தி நீங்கள் என்ன செய்ய முடியும் என்பது பற்றிய கூடுதல் விவரங்களுக்கு, இதனைப் Pulse API reference பார்க்கவும்.

சீரமைப்பு சூழல்கள்

பில்டர் ஒரு அட்டவணை எவ்வாறு கட்டமைக்கப்படுகிறது என்பதைப் பாதிக்கும் சீரமைப்பு சூழல்களைக் கொண்டுள்ளது. சூழல்கள் கூட்டாகவும் சேர்க்கப்படலாம். அவற்றை முயற்சிக்கவும், பல்ஸ்களை எவ்வாறு சீரமைக்கப்படுகின்றன என்பதைக் காண .draw() ஐப் பயன்படுத்தவும்.

Regardless of the alignment context, the duration of the resulting schedule is as short as it can be while including every instruction and following the alignment rules. This still allows some degrees of freedom for scheduling instructions off the "longest path". The examples below illuminate this.

align_left

பில்டர் ஒரு அட்டவணை எவ்வாறு கட்டமைக்கப்படுகிறது என்பதைப் பாதிக்கும் சீரமைப்பு சூழல்களைக் கொண்டுள்ளது. இது இயல்பு நிலை align_left.

[33]:
with pulse.build(backend, name='Left align example') as program:
    with pulse.align_left():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[33]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_28_0.png

D1 இல் பல்ஸ்களுக்கு எப்படி திட்டமிடல் சுதந்திரம் இல்லை என்பதைக் கவனியுங்கள். இரண்டாவது அலைவடிவம் முதல் முடிந்த உடனேயே தொடங்குகிறது. D0 இல் உள்ள துடிப்பு t = 0 மற்றும் t = 100 ஆகியவற்றுக்கு இடையில் எந்த நேரத்திலும் ஒட்டுமொத்த அட்டவணையின் கால அளவை மாற்றாமல் தொடங்கலாம். align_left சூழல் இந்த துடிப்பின் தொடக்க நேரத்தை t = 0 என அமைக்கிறது. இதனை நீங்கள் உரை ஆவணத்தின் இடது-மையப்படுத்துதல் என நினைத்துக் கொள்ளலாம்.

align_right

Unsurprisingly, align_right does the opposite of align_left. It will choose t=100 in the above example to begin the gaussian pulse on D0. Left and right are also sometimes called "as soon as possible" and "as late as possible" scheduling, respectively.

[34]:
with pulse.build(backend, name='Right align example') as program:
    with pulse.align_right():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[34]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_30_0.png

align_equispaced(duration)

ஒரு குறிப்பிட்ட தொகுதியின் காலம் தெரிந்தால், ஒவ்வொரு அறிவுறுத்தலுக்கும் இடையில் சம கால தாமதங்களைச் சேர்க்க align_equispaced ஐப் பயன்படுத்தலாம்.

[35]:
with pulse.build(backend, name='example') as program:
    gaussian_pulse = library.gaussian(100, 0.5, 20)
    with pulse.align_equispaced(2*gaussian_pulse.duration):
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
    pulse.play(gaussian_pulse, pulse.drive_channel(1))
    pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[35]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_32_0.png

align_sequential

இந்த சீரமைப்புச் சூழல் இணையாக வழிமுறைகளைத் திட்டமிடவில்லை. ஒவ்வொரு அறிவுறுத்தலும் முன்னர் சேர்க்கப்பட்ட அறிவுறுத்தலின் முடிவிலேயே தொடங்கும்.

[36]:
with pulse.build(backend, name='example') as program:
    with pulse.align_sequential():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[36]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_34_0.png

கட்டம் மற்றும் அதிர்வெண் ஆஃப்செட்டுகள்

ஒரு சேனலில் நம் பல்ஸ்களின் அதிர்வெண் அல்லது கட்டத்தை தற்காலிகமாக ஈடுசெய்ய நாம் பில்டரைப் பயன்படுத்தலாம்.

[37]:
with pulse.build(backend, name='Offset example') as program:
    with pulse.phase_offset(3.14, pulse.drive_channel(0)):
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        with pulse.frequency_offset(10e6, pulse.drive_channel(0)):
            pulse.play(gaussian_pulse, pulse.drive_channel(0))

program.draw()
[37]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_36_0.png

மேலும் இதனைப் பற்றி அறிந்து கொள்ள, நாங்கள் உங்களை இதனைப் Pulse API reference பார்வையிட ஊக்குவிக்கிறோம்.

[38]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.3
qiskit-aer0.11.2
qiskit-ignis0.7.0
qiskit-ibmq-provider0.19.2
qiskit0.39.4
System information
Python version3.10.6
Python compilerGCC 11.3.0
Python buildmain, Nov 14 2022 16:10:14
OSLinux
CPUs4
Memory (Gb)3.7695083618164062
Thu Dec 22 18:42:20 2022 JST

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.